diff --git "a/include/class-wb\\Claim.php" "b/include/class-wb\\Claim.php" index 4ce1850..97bf446 100644 --- "a/include/class-wb\\Claim.php" +++ "b/include/class-wb\\Claim.php" @@ -1,262 +1,395 @@ . # Wikibase namespace wb; /** * A Claim consists of a Snak and Qualifiers. * * Optionally, it can have qualifiers. * * @see https://www.wikidata.org/wiki/Wikidata:Glossary#Claim */ class Claim { /** * When a claim has not a main snak, we assume that it has this dummy property. * * It's very useful to send claims to be deleted. It works. asd * * @see https://phabricator.wikimedia.org/T203572 */ const DUMMY_PROPERTY = 'ASD-ASD-ASD'; /** - * Snak + * The 'mainsnak' is a Snak * * @var Snak|null */ - //var $mainsnak; + private $mainsnak; - //var $id; + /** + * Claim ID + * + * @var string|null + */ + private $id; /** * Qualifiers collector * * @var Snaks */ private $qualifiers; + /** + * References collector + * + * @TODO: it has also an hash! + * + * @var References + */ + private $references; + /** * Constructor * * @param $mainsnak Snak|null Main snak */ public function __construct( $mainsnak = null ) { - // avoid to set a NULL value in order to do not send { 'mainsnak': null } to the API + + // eventually initialize mainsnak if( $mainsnak ) { $this->setMainsnak( $mainsnak ); } // initialize snaks collector $this->qualifiers = new Snaks(); + + // initialize references collector + $this->references = new References(); + } + + /** + * Check if there is a mainsnak + * + * @return boolean + */ + public function hasMainsnak() { + return isset( $this->mainsnak ); } /** * Get the mainsnak * * @return Snak|null */ public function getMainsnak() { - return isset( $this->mainsnak ) ? $this->mainsnak : null; + return $this->mainsnak; } /** * Get a property. Also a dummy one. NOW. asd * * @return string */ public function getPropertyAlsoDummy() { $snak = $this->getMainsnak(); return $snak ? $snak->getProperty() : self::DUMMY_PROPERTY; } /** * Set the mainsnak * * @param $mainsnak Snak|null * @return self */ public function setMainsnak( $mainsnak ) { $this->mainsnak = $mainsnak; return $this; } /** * Set the qualifiers (snaks) * - * @param $qualifiers + * @param object $qualifiers * @return self */ public function setQualifiers( Snaks $qualifiers ) { $this->qualifiers = $qualifiers; return $this; } + /** + * Set the references + * + * @param object $references + * @return self + */ + public function setReferences( References $references ) { + $this->references = $references; + return $this; + } + /** * Add a qualifier (snak) * * @param object $qualifier Qualifier (snak) - * @return array + * @return self */ public function addQualifier( Snak $qualifier ) { $this->qualifiers->add( $qualifier ); return $this; } + /** + * Add a reference + * + * @param object $reference Reference + * @return self + */ + public function addReference( Reference $reference ) { + $this->references->add( $reference ); + return $this; + } + /** * Check if the claim has at least a qualifier * * @return boolean */ public function hasQualifiers() { return !$this->qualifiers->isEmpty(); } + /** + * Check if the claim has at least a reference + * + * @return boolean + */ + public function hasReferences() { + return !$this->references->isEmpty(); + } + /** * Check if there are qualifiers related to a property * * @param $property string e.g. 'P123' * @return boolean */ public function hasQualifiersInProperty( $property ) { return $this->qualifiers->hasInProperty( $property ); } + /** + * Check if there are references related to a property + * + * @param $property string e.g. 'P123' + * @return boolean + */ + public function hasReferencesInProperty( $property ) { + return $this->references->hasInProperty( $property ); + } + /** * Get all the qualifiers indexed by property (they are snaks) * * @return array */ public function getQualifiers() { return $this->qualifiers->getAll(); } + /** + * Get all the qualifiers indexed by property (they are snaks) + * + * @return array + */ + public function getReferences() { + return $this->references->getAll(); + } + /** * Get all the qualifiers that are related to a property (they are snaks) * * @param $property string e.g. 'P123' * @return array */ public function getQualifiersInProperty( $property ) { return $this->qualifiers->getInProperty( $property ); } + /** + * Get all the references that are related to a property (they are snaks) + * + * @param $property string e.g. 'P123' + * @return array + */ + public function getReferencesInProperty( $property ) { + return $this->references->getInProperty( $property ); + } + + /** + * Check if there is an ID + * + * @return boolean + */ + public function hasID() { + return isset( $this->id ); + } + /** * Set the claim ID * * @param $id string * @return string */ public function setID( $id ) { $this->id = $id; return $this; } /** * Get the claim ID * * @return string */ public function getID() { - if( empty( $this->id ) ) { + if( !$this->hasID() ) { throw new \Exception( 'missing id' ); } return $this->id; } /** * Check if this claim is marked for removal * * @return array */ public function isMarkedForRemoval() { return isset( $this->remove ) && $this->remove; } /** * Mark this claim as to be remove * * @see https://www.wikidata.org/w/api.php?action=help&modules=wbeditentity * @return self */ public function markForRemoval() { $this->remove = 1; return $this; } /** * Clone this claim and obtain a claim marked for removal * * @return self */ public function cloneForRemoval() { return ( new self() ) ->setID( $this->getID() ) ->markForRemoval(); } /** * Create a claim from raw data returned from API responses * * @param $data array * @return self */ public static function createFromData( $data ) { // wtf is this shit - if( ! isset( $data[ 'mainsnak' ] ) ) { + if( ! isset( $data['mainsnak'] ) ) { throw new WrongDataException( __CLASS__ ); } // initialize the claim $claim = new static( Snak::createFromData( $data[ 'mainsnak' ] ) ); // add qualifiers - if( isset( $data[ 'qualifiers' ] ) ) { - foreach( $data[ 'qualifiers' ] as $property => $qualifier_raws ) { + if( isset( $data['qualifiers'] ) ) { + foreach( $data['qualifiers'] as $property => $qualifier_raws ) { foreach( $qualifier_raws as $qualifier_raw ) { $qualifier = Snak::createFromData( $qualifier_raw ); $claim->addQualifier( $qualifier ); } } } + // add references + if( isset( $data['references'] ) ) { + $claim->setReferences( References::createFromData( $data['references'] ) ); + } + // claim ID if( isset( $data[ 'id' ] ) ) { $claim->setID( $data[ 'id' ] ); } return $claim; } + /** + * Convert this object to an associative array suitable for JSON encoding + * + * @return array + */ + public function toData() { + + $data = []; + + // mainsnak + if( $this->hasMainsnak() ) { + $data['mainsnak'] = $this->getMainsnak()->toData(); + } + + // id + if( $this->hasID() ) { + $data['id'] = $this->getID(); + } + + // qualifiers + if( $this->hasQualifiers() ) { + $data['qualifiers'] = $this->qualifiers->toData(); + } + + // references + if( $this->hasReferences() ) { + $data['references'] = $this->references->toData(); + } + + return $data; + } + /** * @override */ public function __toString() { $snak = $this->getMainsnak(); if( $snak ) { return $snak->getDataValue(); } $id = $this->getID(); if( $id && $this->isMarkedForRemoval() ) { return "remove claim id = $id"; } throw new \Exception( 'empty claim' ); } } diff --git "a/include/class-wb\\Claims.php" "b/include/class-wb\\Claims.php" index 9673456..3cdfd0d 100644 --- "a/include/class-wb\\Claims.php" +++ "b/include/class-wb\\Claims.php" @@ -1,124 +1,139 @@ . # Wikibase namespace wb; /** * Claim collector */ class Claims { /** * All the claims * * @TODO: check if it can be declared as private without breaking casts :^) * @var array */ public $claims = []; /** * Constructor * * @param $claims array */ public function __construct( $claims = [] ) { foreach( $claims as $claim ) { $this->add( $claim ); } } /** * Add a claim * * @param $claim $Claim * @return self */ public function add( Claim $claim ) { $this->claims[] = $claim; return $this; } /** * Count all * * @return int */ public function count() { return count( $this->claims ); } /** * Get all the claims in a certain property * * @param $property string * @return array */ public function getInProperty( $property ) { $claims = []; foreach( $this->claims as $claim ) { if( $claim->getMainSnak()->getProperty() === $property ) { $claims[] = $claim; } } return $claims; } /** * Check if there are claims in a certain property * * @param $property string * @return bool */ public function haveProperty( $property ) { foreach( $this->claims as $claim ) { $snak = $claim->getMainsnak(); if( $snak && $snak->getProperty() === $property ) { return true; } } return false; } /** * Get all the claims * * @return array */ public function getAll() { return $this->claims; } /** * Get all the claims indexed by their property * * Note that you can obtain also a dummy property * * @return array */ public function getAllGrouped() { $properties = []; foreach( $this->getAll() as $claim ) { $property = $claim->getPropertyAlsoDummy(); if( ! isset( $properties[ $property ] ) ) { $properties[ $property ] = []; } $properties[ $property ][] = $claim; } return $properties; } + + /** + * Convert this object to an associative array suitable for JSON encoding + * + * @return array + */ + public function toData() { + $all = $this->getAllGrouped(); + foreach( $all as & $claims ) { + foreach( $claims as & $claim ) { + $claim = $claim->toData(); + } + } + return $all; + } } diff --git "a/include/class-wb\\DataModel.php" "b/include/class-wb\\DataModel.php" index 483843b..bf50638 100644 --- "a/include/class-wb\\DataModel.php" +++ "b/include/class-wb\\DataModel.php" @@ -1,527 +1,527 @@ . # Wikibase namespace wb; use \mw\WikibaseSite; /** * Wikibase data container */ class DataModel { /** * Dependency injection of a Wikibase site * * @var WikibaseSite|null */ private $site; /** * Dependency injection of the entity Q-ID this data refers to * * @var string|null */ private $entityID; /** * @var Labels */ private $labels; /** * @var Descriptions */ private $descriptions; /* * @var Claims */ private $claims; /* * @var Sitelinks */ private $sitelinks; /** * Constructor * * @param $site WikibaseSite * @param $entity_id string Entity Q-ID */ public function __construct( $site = null, $entity_id = null ) { $this->site = $site; $this->labels = new Labels(); $this->descriptions = new Descriptions(); $this->claims = new Claims(); $this->setSitelinks( new Sitelinks() ); if( $entity_id ) { $this->setEntityID( $entity_id ); } } /** * Get the Wikibase site * * @return WikibaseSite */ public function getWikibaseSite() { if( ! isset( $this->site ) ) { throw new \Exception( 'can\'t access to undefined Wikibase site' ); } return $this->site; } /** * Set the entity Q-ID * * @param $entity_id string Q-ID * @return self */ public function setEntityID( $entity_id ) { $this->entityID = $entity_id; return $this; } /** * Check if it has the entity Q-ID * * @return bool */ public function hasEntityID() { return isset( $this->entityID ); } /** * Get the entity Q-ID * * @return string */ public function getEntityID() { if( isset( $this->entityID ) ) { return $this->entityID; } throw new \Exception( 'undefined entity ID' ); } /** * Get all the labels * * @return array */ public function getLabels() { return $this->labels->getAll(); } /** * Get all the descriptions * * @return array */ public function getDescriptions() { return $this->descriptions->getAll(); } /** * Get all the claims * * @return array */ public function getClaims() { return $this->claims->getAll(); } /** * Get all the sitelinks * * @return array */ public function getSitelinks() { return $this->sitelinks; } /** * Set all the sitelinks * * @param $sitelinks * @return array */ public function setSitelinks( Sitelinks $sitelinks ) { $this->sitelinks = $sitelinks; return $this; } /** * Get all the claims grouped by property * * n.b. The claims without a property will be indexed by 'asd' * * @return array */ public function getClaimsGrouped() { return $this->claims->getAllGrouped(); } /** * Add a claim * * @param $claim Claim * @return self */ public function addClaim( $claim ) { $this->claims->add( $claim ); return $this; } /** * Set claims * * @param $claims array * @return self */ public function setClaims( $claims ) { $this->claims->set( $claims ); return $this; } /** * Check if some claims exist in a certain property * * @param $property string * @return bool */ public function hasClaimsInProperty( $property ) { return $this->claims->haveProperty( $property ); } /** * Get claims in a property * * @param $property string * @return array */ public function getClaimsInProperty( $property ) { return $this->claims->getInProperty( $property ); } /** * Has sitelink in site * * @param $site string * @return boolean */ public function hasSitelinkInSite( $site ) { return false !== $this->getSitelinkInSite( $site ); } /** * Count all the claims * * @return int */ public function countClaims() { return $this->claims->count(); } /** * Check if it's empty * * @return bool */ public function isEmpty() { return ! $this->countClaims() && ! $this->getLabels() && ! $this->getDescriptions() && ! $this->sitelinks->getAll(); } /** * Check if a label of a certain language exists * * @param $language string * @return bool */ public function hasLabelInLanguage( $language ) { return $this->labels->have( $language ); } /** * Set, delete, preserve if it exists, a label. */ public function setLabel( $label ) { $this->labels->set( $label ); return $this; } /** * Check if a label of a certain language exists * * @param $language string * @return bool */ public function hasDescriptionInLanguage( $language ) { return $this->descriptions->have( $language ); } /** * Set, delete, preserve if it exists, a description. * * @param $description Description * @return self */ public function setDescription( $description ) { $this->descriptions->set( $description ); return $this; } /** * Get a pure data rappresentation * * @param $clear bool Flag to be enabled to strip out empty elements * @return array */ public function get( $clear = false ) { $data = [ 'labels' => $this->getLabels(), // TODO: $this->getLabelsData() 'descriptions' => $this->getDescriptions(), // TODO: $this->getDescriptionsData() 'sitelinks' => $this->sitelinks->toData(), - 'claims' => $this->getClaimsGrouped() + 'claims' => $this->claims->toData(), ]; foreach( $data as $k => $v ) { if( 0 === count( $v ) ) { unset( $data[ $k ] ); } } if( $clear ) { $data['clear'] = true; } return $data; } /** * Get a JSON rappresentation of this data * * @param $args int Options for json_encode() * @return string */ public function getJSON( $options = 0 ) { return json_encode( $this->get(), $options ); } /** * Get a JSON rappresentation of this data (without empty elements) * * @param $args int Options for json_encode() * @return string */ public function getJSONClearing( $args = null ) { return json_encode( $this->get( true ), $args ); } /** * Print the changes in order to confirm them * * @return self */ public function printChanges() { if( $this->hasEntityID() ) { \cli\Log::info( "-- changes for {$this->getEntityID()} --" ); } $labels = $this->getLabels(); if( $labels ) { \cli\Log::info( "labels:" ); foreach( $labels as $label ) { \cli\Log::info( "\t" . $label ); } } $descriptions = $this->getDescriptions(); if( $descriptions ) { mw\Log::info( "descriptions: "); foreach( $descriptions as $description ) { \cli\Log::info( "\t" . $description ); } } $properties = $this->getClaimsGrouped(); if( $properties ) { \cli\Log::info( "claims:" ); foreach( $properties as $property => $claims ) { if( $property === Claim::DUMMY_PROPERTY ) { \cli\Log::info( "\twithout property:" ); } else { \cli\Log::info( "\t$property:" ); } foreach( $claims as $claim ) { $snak = $claim->getMainsnak(); if( $snak ) { \cli\Log::info( "\t\t" . $snak->getDataValue() ); } else { \cli\Log::info( "\t\t" . $claim ); } } } } return $this; } /** * Generate an edit summary for the data contained in here * * @return string */ public function getEditSummary() { $changes = []; $labels = $this->getLabels(); if( $labels ) { $changes[] = $this->labels->__toString(); } $descriptions = $this->getDescriptions(); if( $descriptions ) { $changes[] = $this->descriptions->__toString(); } $sitelinks = $this->getSitelinks(); if( $sitelinks->getAll() ) { $changes[] = $this->sitelinks->__toString(); } foreach( $this->getClaims() as $claim ) { $snak = $claim->getMainsnak(); if( $snak ) { $changes[] = '+' . $snak; } elseif( $claim->isMarkedForRemoval() ) { $changes[] = '-claim id=' . $claim->getID(); } else { throw new \Exception( "unexpected undefined main snak, that it's allowed only in claims marked for deletion" ); } } return implode( '; ', $changes ); } /** * Obtain an empty clone of this data container * * @return self */ public function cloneEmpty() { $cloned = new self( $this->getWikibaseSite() ); if( $this->hasEntityID() ) { $cloned->setEntityID( $this->getEntityID() ); } return $cloned; } /** * Edit a Wikibase entity using the wbgetentities API * * It works only if the Wikibase site is specified * You can not specify the 'id' if you specified the entity ID * * @param $data array API data request * @return mixed * @see https://www.wikidata.org/w/api.php?action=help&modules=wbeditentity */ public function editEntity( $data = [] ) { // can auto-generate a summary if( empty( $data['summary'] ) ) { $data['summary'] = $this->getEditSummary(); } $data = array_replace( [ 'id' => $this->hasEntityID() ? $this->getEntityID() : null, 'data' => $this->getJSON(), ], $data ); return $this->getWikibaseSite()->editEntity( $data ); } /** * Static constructor from an associative array * * This method is used to import an array obtained * from JSON-decoding the Wikibase wbgetentity API result * * @param $data array Response of wbgetentity API result * @param $site WikibaseSite * @param $entity_id string * @return self * @see https://www.wikidata.org/w/api.php?action=help&modules=wbgetentities */ public static function createFromData( $data, $site = null, $entity_id = null ) { $dataModel = new self( $site, $entity_id ); if( ! empty( $data[ 'labels' ] ) ) { // TODO: Labels::createFromData() foreach( $data[ 'labels' ] as $label ) { $dataModel->setLabel( Label::createFromData( $label ) ); } } if( ! empty( $data[ 'descriptions' ] ) ) { // TODO: Descriptions::createFromData() foreach( $data[ 'descriptions' ] as $description ) { $dataModel->setDescription( Description::createFromData( $description ) ); } } if( ! empty( $data[ 'claims' ] ) ) { // TODO: Claims::createFromData() foreach( $data[ 'claims' ] as $claims ) { foreach( $claims as $claim ) { $dataModel->addClaim( Claim::createFromData( $claim ) ); } } } if( ! empty( $data[ 'sitelinks' ] ) ) { $dataModel->setSitelinks( Sitelinks::createFromData( $data[ 'sitelinks' ] ) ); } return $dataModel; } /** * Static constructor from an object * * @param $object object * @param $site WikibaseSite * @param $entity_id string * @return self */ public static function createFromObject( $object, $site = null, $entity_id = null ) { return self::createFromData( self::object2array( $object ), $site, $entity_id ); } /** * Convert an object to an array * * @param $object object * @return array */ private static function object2array( $object ) { if( ! is_object( $object) && ! is_array( $object ) ) { return $object; } $array = []; foreach( $object as $k => $v ) { $array[ $k ] = self::object2array( $v ); } return $array; } } diff --git "a/include/class-wb\\DataType.php" "b/include/class-wb\\DataType.php" index 74a12e5..efdebee 100644 --- "a/include/class-wb\\DataType.php" +++ "b/include/class-wb\\DataType.php" @@ -1,40 +1,40 @@ . # Wikibase namespace wb; /** - * A DataType. + * A wikibase DataValue's type * * @see https://www.wikidata.org/wiki/Special:ListDatatypes */ class DataType { const STRING = 'string'; const URL = 'url'; const TIME = 'time'; const QUANTITY = 'quantity'; const MONOLINGUAL_TEXT = 'monolingualtext'; const GLOBE_COORDINATE = 'globecoordinate'; const EXTERNAL_ID = 'external-id'; const COMMONS_MEDIA = 'commonsMedia'; const ITEM = 'wikibase-item'; const ENTITY_ID = 'wikibase-entityid'; const PROPERTY = 'wikibase-property'; } diff --git "a/include/class-wb\\Reference.php" "b/include/class-wb\\Reference.php" new file mode 100644 index 0000000..f865236 --- /dev/null +++ "b/include/class-wb\\Reference.php" @@ -0,0 +1,232 @@ +. + +# Wikibase +namespace wb; + +/** + * Reference + * + * A reference is a collection of snaks. + */ +class Reference { + + /** + * Identifier of this reference + * + * @var string + */ + private $hash; + + /** + * All the snaks + * + * @var object + */ + private $snaks; + + /** + * Snaks order + * + * It's an array of property names. e.g. [ 'P123' , ... ] + * + * @var array + */ + private $snaksOrder; + + /** + * Constructor + * + * @param array $snaks + */ + public function __construct( $snaks = [] ) { + $this->snaks = new Snaks( $snaks ); + } + + /** + * Check if the snak has an hash + * + * @return bool + */ + public function hasHash() { + return isset( $this->hash ); + } + + /** + * Get the hash + * + * @return string|null + */ + public function getHash() { + return $this->hash; + } + + /** + * Set the hash + * + * @param $hash string + * @return self + */ + public function setHash( $hash ) { + $this->hash = $hash; + return $this; + } + + /** + * Add a snak + * + * @param object $snak + * @return self + */ + public function add( Snak $snak ) { + $this->snaks->add( $snak ); + return $this; + } + + /** + * Count all the snaks + * + * @return int + */ + public function count() { + return $this->snaks->count(); + } + + /** + * Get all the snaks in a certain property + * + * @param $property string + * @return array + */ + public function getSnaksInProperty( $property ) { + return $this->snaks->getInProperty( $property ); + } + + /** + * Check if there are snaks in a certain property + * + * @param $property string + * @return bool + */ + public function hasSnaksInProperty( $property ) { + return $this->snaks->hasInProperty( $property ); + } + + /** + * Get all the snaks indexed by property + * + * @return array + */ + public function getSnaksByProperty() { + return $this->snaks->getAllByProperty(); + } + + /** + * Check if the snaks order is specified + * + * @return boolean + */ + public function hasSnaksOrder() { + return isset( $this->snaksOrder ); + } + + /** + * Check if the reference is empty + * + * @return boolean + */ + public function isEmpty() { + return $this->snaks->isEmpty(); + } + + /** + * Get the snaks order + * + * @return array|null + */ + public function getSnaksOrder() { + return $this->snaksOrder; + } + + /** + * Set the snaks order + * + * @param array $properties + * @return self + */ + public function setSnaksOrder( $properties ) { + $this->snaksOrder = $properties; + return $this; + } + + /** + * Constructor from a raw object retrieved from API results + * + * @param object $reference_raw + * @return self + */ + public static function createFromData( $reference_raw ) { + $reference = new self(); + + // reference snaks + if( !isset( $reference_raw['snaks'] ) ) { + throw new WrongDataException( 'bad reference object: no snaks field' ); + } + foreach( $reference_raw['snaks'] as $property => $snaks_raw ) { + foreach( $snaks_raw as $snak_raw ) { + $snak = Snak::createFromData( $snak_raw ); + $reference->add( $snak ); + } + } + + // reference hash + if( isset( $reference_raw['hash'] ) ) { + $reference->setHash( $reference_raw['hash'] ); + } + + // reference snaks order (array of properties) + if( isset( $reference_raw['snaks-order'] ) ) { + $reference->setSnaksOrder( $reference_raw['snaks-order'] ); + } + + return $reference; + } + + /** + * Convert this object to an associative array suitable for JSON encoding + * + * @return array + */ + public function toData() { + $data = []; + + // reference hash + if( $this->hasHash() ) { + $data['hash'] = $this->getHash(); + } + + // reference snaks + $data['snaks'] = $this->snaks->toData(); + + // snaks order (property list) + if( $this->hasSnaksOrder() ) { + $data['snaks-order'] = $this->getSnaksOrder(); + } + + return $data; + } +} diff --git "a/include/class-wb\\References.php" "b/include/class-wb\\References.php" new file mode 100644 index 0000000..14c13e1 --- /dev/null +++ "b/include/class-wb\\References.php" @@ -0,0 +1,98 @@ +. + +# Wikibase +namespace wb; + +/** + * References collector + * + * It is very similar to the claims collector. + * + * References are assigned to a claim. + */ +class References { + + private $references = []; + + /** + * Constructor + * + * @param array $references + */ + public function __construct( $references = [] ) { + foreach( $references as $reference ) { + $this->add( $reference ); + } + } + + /** + * Add a reference + * + * @param object $reference + * @return self + */ + public function add( Reference $reference ) { + $this->references[] = $reference; + return $this; + } + + /** + * Get all the references + * + * @return array + */ + public function getAll() { + return $this->references; + } + + /** + * Check if it does not contain references + * + * @return boolean + */ + public function isEmpty() { + return empty( $this->references ); + } + + /** + * Convert this object to an associative array suitable for JSON encoding + * + * @return array + */ + public function toData() { + $data = []; + foreach( $this->getAll() as $reference ) { + $data[] = $reference->toData(); + } + return $data; + } + + /** + * Constructor from a raw object retrieved from API results + * + * @param array $references_raw + * @return self + */ + public static function createFromData( $references_raw ) { + $references = new self(); + foreach( $references_raw as $reference_raw ) { + $references->add( Reference::createFromData( $reference_raw ) ); + } + return $references; + } +} diff --git "a/include/class-wb\\Snak.php" "b/include/class-wb\\Snak.php" index c443acb..9f76f6f 100644 --- "a/include/class-wb\\Snak.php" +++ "b/include/class-wb\\Snak.php" @@ -1,219 +1,245 @@ . # Wikibase namespace wb; /** * A generic Snak is a combination of a property and a datatype + datavalue. * * A Snak is part of a Claim or a Reference. It's based on a DataValue. */ class Snak { - //var $hash; - var $snaktype; - var $property; - var $datatype; - var $datavalue; + private $hash; + + private $snaktype; + + private $property; + + private $datatype; + + private $datavalue; /** * @param $snaktype string * @param $property string * @param $datatype string * @param $datavalue mixed */ public function __construct( $snaktype, $property, $datatype, $datavalue = null ) { $this->setSnakType( $snaktype ) ->setProperty( $property ) ->setDataType( $datatype ); if( null !== $datavalue ) { $this->setDataValue( $datavalue ); } } /** * Get the property * * @return string */ public function getProperty() { return $this->property; } /** * Get the snak type */ public function getSnakType() { return $this->snaktype; } /** * Get the data type */ public function getDataType() { return $this->datatype; } /** * Get the data value * * @return DataValue */ public function getDataValue() { return $this->datavalue; } /** * Set the snak type * * @param $snaktype * @param self */ public function setSnakType( $snaktype ) { $this->snaktype = $snaktype; return $this; } /** * Set the property * * @param $property string * @param self */ public function setProperty( $property ) { $this->property = $property; return $this; } /** * Get an human property label, if available in cache */ public function getPropertyLabel() { return self::propertyLabel( $this->getProperty() ); } /** * Set the data type * * @param $datatype * @param self */ public function setDataType( $datatype ) { $this->datatype = $datatype; return $this; } /** * Set the data value * * @param $datavalue DataValue * @param self */ public function setDataValue( DataValue $datavalue ) { $this->datavalue = $datavalue; return $this; } /** * Check if the snak has an hash * * @return bool */ public function hasHash() { return isset( $this->hash ); } /** * Get the hash * * @return string|null */ public function getHash() { return $this->hash; } /** * Set the hash * * @param $hash string * @return self */ public function setHash( $hash ) { $this->hash = $hash; return $this; } /** * Get a wikilink to this property * * @return string */ protected function getPropertyWLink() { $prop = $this->getProperty(); $label = $this->getPropertyLabel(); if( ! $label ) { $label = $prop; } return sprintf( '[[P:%s|%s]]', $prop, $label ); } /** * Try to read the property label from the cache * * @TODO: ask also the site * @return string|false */ public static function propertyLabel( $property ) { return \wm\Wikidata::propertyLabel( $property ); } /** * Create a snak from raw data * * @param $data array * @return self */ public static function createFromData( $data ) { if( ! isset( $data['snaktype'], $data['property'], $data['datatype'] ) ) { + var_dump( $data ); throw new WrongDataException( __CLASS__ ); } $snak = new self( $data['snaktype'], $data['property'], $data['datatype'] ); if( isset( $data['datavalue'] ) ) { $snak->setDataValue( DataValue::createFromData( $data['datavalue'] ) ); } if( isset( $data['hash'] ) ) { $snak->setHash( $data['hash'] ); } return $snak; } + /** + * Convert this object to an associative array suitable for JSON encoding + * + * @return array + */ + public function toData() { + $data = []; + + // it may have an hash + if( $this->hasHash() ) { + $data['hash'] = $this->getHash(); + } + + $data['snaktype'] = $this->getSnakType(); + $data['property'] = $this->getProperty(); + $data['datatype'] = $this->getDataType(); + $data['datavalue'] = $this->getDataValue(); + + return $data; + } + /** * @return string */ public function __toString() { return sprintf( '%s: %s', $this->getPropertyWLink(), $this->getDataValue() ); } } diff --git "a/include/class-wb\\SnakTime.php" "b/include/class-wb\\SnakTime.php" index e0f5b7d..df86744 100644 --- "a/include/class-wb\\SnakTime.php" +++ "b/include/class-wb\\SnakTime.php" @@ -1,55 +1,74 @@ . # Wikibase namespace wb; /** * A Snak for a string. */ class SnakTime extends Snak { /** + * Constructor + * + * Note that: WHAT THE FUCK I'M READING HOLY CRAP WIKIBASE + * * @param $property string Property as 'P123' - * @param $time string example "+1539-00-00T00:00:00Z" - * @param $precision int + * @param $time string example "+1539-00-00T00:00:00Z" As default it's the current datetime + * @param $precision int As default it's 11 (days) * 0: 1 Gigayear * 1: 100 Megayears * 2: 10 Megayears * 3: Megayear * 4: 100 Kiloyears * 5: 10 Kiloyears * 6: Kiloyear * 7: 100 years * 8: 10 years * 9: years * 10: months * 11: days * @param $args array example: * timezone: 0, * before: 0, * after: 0, * precision: 9, * calendarmodel: "Q1985786" * @see https://www.mediawiki.org/wiki/Wikibase/DataModel/JSON */ - public function __construct( $property, $time, $precision, $args = [] ) { - return parent::__construct( 'value', $property, DataType::QUANTITY, + public function __construct( $property, $time = null, $precision = 11, $args = [] ) { + + // as default take current date and time + if( !$time ) { + $time = new \DateTime(); + $time->setTimezone( new \DateTimeZone( 'UTC' ) ); + + // Note that: WHAT THE FUCK I'M READING HOLY CRAP WIKIBASE + $time->setTime( 0, 0 ); + } + + // accept both a string or a datetime object + if( $time instanceof \DateTime ) { + $time = $time->format( '+Y-m-d\TH:i:s\Z' ); + } + + return parent::__construct( 'value', $property, DataType::TIME, new DataValueTime( $time, $precision, $args ) ); } } diff --git "a/include/class-wb\\DataType.php" "b/include/class-wb\\SnakURL.php" similarity index 55% copy from "include/class-wb\\DataType.php" copy to "include/class-wb\\SnakURL.php" index 74a12e5..4df06f6 100644 --- "a/include/class-wb\\DataType.php" +++ "b/include/class-wb\\SnakURL.php" @@ -1,40 +1,40 @@ . # Wikibase namespace wb; /** - * A DataType. - * - * @see https://www.wikidata.org/wiki/Special:ListDatatypes + * A Snak for an URL */ -class DataType { - - const STRING = 'string'; - const URL = 'url'; - const TIME = 'time'; - const QUANTITY = 'quantity'; - const MONOLINGUAL_TEXT = 'monolingualtext'; - const GLOBE_COORDINATE = 'globecoordinate'; - const EXTERNAL_ID = 'external-id'; - const COMMONS_MEDIA = 'commonsMedia'; - const ITEM = 'wikibase-item'; - const ENTITY_ID = 'wikibase-entityid'; - const PROPERTY = 'wikibase-property'; +class SnakURL extends Snak { + /** + * Constructor + * + * @param string $property Wikibase property name e.g. 'P123' + * @param string $url + */ + public function __construct( $property, $url ) { + return parent::__construct( + 'value', + $property, + DataType::URL, + new DataValueString( $url ) + ); + } } diff --git "a/include/class-wb\\Snaks.php" "b/include/class-wb\\Snaks.php" index f964dde..cfef4dd 100644 --- "a/include/class-wb\\Snaks.php" +++ "b/include/class-wb\\Snaks.php" @@ -1,102 +1,131 @@ . # Wikibase namespace wb; /** * A Snak collector * * @since 2019-07-18 */ class Snaks { /** * All the Snaks indexed by property */ private $snaks = []; /** * Constructor * * @param array $snaks Initial set of snaks */ public function __construct( $snaks = [] ) { foreach( $snaks as $snak ) { $this->add( $snak ); } } /** * Add a Snak * * @param object $snak * @return this */ public function add( Snak $snak ) { // eventually init the property container of snaks $property = $snak->getProperty(); if( !isset( $this->snaks[ $property ] ) ) { $this->snaks[ $property ] = []; } // append $this->snaks[ $property ][] = $snak; return $this; } /** * Get all the snaks from a certain property * * @param string $property Property name e.g. 'P123' * @return array */ public function getInProperty( $property ) { if( $this->hasInProperty( $property ) ) { return $this->snaks[ $property ]; } return []; } /** * Check if there are some snaks in this property */ public function hasInProperty( $property ) { return !empty( $this->snaks[ $property ] ); } /** * Check if there are not snaks * * @return boolean */ public function isEmpty() { return !empty( $this->snaks ); } /** * Get all the snaks indexed by property * * @return array */ - public function getAll() { + public function getAllByProperty() { return $this->snaks; } + /** + * Count all the snaks + * + * @return int + */ + public function count() { + $n = 0; + foreach( $this->getAll() as $property => $snaks ) { + $n += count( $snaks ); + } + return $n; + } + + /** + * Convert this object to an associative array suitable for JSON encoding + * + * @return array + */ + public function toData() { + $data = []; + foreach( $this->getAllByProperty() as $property => $snaks ) { + $data[ $property ] = []; + foreach( $snaks as $snak ) { + $data[ $property ][] = $snak->toData(); + } + } + return $data; + } + } diff --git "a/include/class-wb\\Statement.php" "b/include/class-wb\\Statement.php" index 08a5134..f246b72 100644 --- "a/include/class-wb\\Statement.php" +++ "b/include/class-wb\\Statement.php" @@ -1,87 +1,162 @@ . # Wikibase namespace wb; /** * A Statement is a type of Claim with references and a rank. * * @see https://www.wikidata.org/wiki/Wikidata:Glossary#Statement */ class Statement extends Claim { - //var $id; - var $type = 'statement'; - var $rank = 'normal'; - var $references = []; + /** + * ID of the statement (if any) + * + * @var string|null + */ + private $id; + /** + * Type of the statement + * + * I think that it's hardcoded to 'statement' in Wikibase. + * + * @var string + */ + private $type = 'statement'; + + /** + * Rank of this statement + * + * @var string + */ + private $rank = 'normal'; + + /** + * Get the type + * + * @return string + */ public function getType() { return $this->type; } + /** + * Get the rank + * + * @return string + */ public function getRank() { return $this->rank; } - public function getReferences() { - return $this->references; - } - + /** + * Check if this statement has an ID + * + * @return boolean + */ public function hasID() { return isset( $this->id ); } + /** + * Get the ID (if any) + * + * @return string|null + */ public function getID() { return $this->id; } + /** + * Get the ID (if any) + * + * @return string|null + */ + public function setID( $id ) { + $this->id = $id; + } + + /** + * Set the type + * + * @param string $type + * @return self + */ public function setType( $type ) { $this->type = $type; return $this; } + /** + * Set a rank + * + * @param string $rank + * @return self + */ public function setRank( $rank ) { $this->rank = $rank; return $this; } - public function setReferences( $references ) { - $this->references = $references; - return $this; - } - - public function setID( $id ) { - $this->id = $id; - } - + /** + * Create a statement from raw data returned from API responses + * + * @param $data array + * @return self + */ public static function createFromData( $data ) { if( ! isset( $data['type'], $data['rank'] ) ) { throw new WrongDataException( __CLASS__ ); } $statement = parent::createFromData( $data ); $statement->setType( $data['type'] ); $statement->setRank( $data['rank'] ); if( $data['id'] ) { $statement->setID( $data['id'] ); } if( $data['references'] ) { - $statement->setReferences( $data['references'] ); + $statement->setReferences( References::createFromData( $data['references'] ) ); } return $statement; } + + /** + * Convert this object to an associative array suitable for JSON encoding + * + * @return array + */ + public function toData() { + $data = parent::toData(); + + // statement ID + if( $this->hasID() ) { + $data['id'] = $this->getID(); + } + + // statement type + $data['type'] = $this->getType(); + + // statement rank + $data['rank'] = $this->getRank(); + + return $data; + } + }