├── i18n ├── ko.json ├── zh-hans.json ├── ja.json ├── zh-hant.json ├── he.json ├── vi.json ├── nb.json ├── uk.json ├── ru.json ├── sv.json ├── be-tarask.json ├── eu.json ├── fa.json ├── mk.json ├── nl.json ├── pt-br.json ├── pt.json ├── ast.json ├── en.json ├── gl.json ├── it.json ├── ms.json ├── qqq.json ├── dsb.json ├── hsb.json ├── oc.json ├── ia.json ├── es.json ├── roa-tara.json ├── tl.json ├── bcl.json ├── ilo.json ├── fr.json ├── ksh.json └── de.json ├── .gitignore ├── src ├── Hashable.php ├── Comparable.php ├── Immutable.php ├── Typeable.php └── Language │ ├── Selection │ ├── SelectionRequest.php │ ├── SubjectSelection.php │ └── PropertySelection.php │ ├── Description │ ├── Conjunction.php │ ├── Disjunction.php │ ├── Description.php │ ├── AnyValue.php │ ├── ValueDescription.php │ ├── DescriptionCollection.php │ └── SomeProperty.php │ ├── Option │ ├── SortOptions.php │ ├── SortExpression.php │ ├── PropertyValueSortExpression.php │ └── QueryOptions.php │ └── Query.php ├── .scrutinizer.yml ├── .travis.yml ├── Tests ├── bootstrap.php ├── Phpunit │ ├── Language │ │ ├── Description │ │ │ ├── AnyValueTest.php │ │ │ ├── ConjunctionTest.php │ │ │ ├── DisjunctionTest.php │ │ │ ├── ValueDescriptionTest.php │ │ │ ├── SomePropertyTest.php │ │ │ ├── DescriptionTest.php │ │ │ └── DescriptionCollectionTest.php │ │ ├── Selection │ │ │ ├── SubjectSelectionTest.php │ │ │ ├── PropertySelectionTest.php │ │ │ └── SelectionRequestTest.php │ │ ├── Option │ │ │ ├── SortExpressionTest.php │ │ │ ├── SortOptionsTest.php │ │ │ ├── QueryOptionsTest.php │ │ │ └── PropertyValueSortExpressionTest.php │ │ └── QueryTest.php │ └── AskTestCase.php └── testLoader.php ├── Ask.php ├── phpunit.xml.dist ├── composer.json ├── Ask.mw.php ├── Ask.i18n.php ├── README.md └── COPYING /i18n/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "아라" 5 | ] 6 | }, 7 | "ask-desc": "Ask 쿼리 언어의 PHP 구현을 포함하는 라이브러리" 8 | } -------------------------------------------------------------------------------- /i18n/zh-hans.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Qiyue2001" 5 | ] 6 | }, 7 | "ask-desc": "包含以PHP实现的ASK查询语言的库" 8 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | *~ 3 | *.kate-swp 4 | 5 | !.gitignore 6 | !.gitreview 7 | 8 | !*.yml 9 | 10 | vendor/ 11 | composer.phar 12 | composer.lock 13 | 14 | build/ -------------------------------------------------------------------------------- /i18n/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Shirayuki" 5 | ] 6 | }, 7 | "ask-desc": "クエリ言語 Ask の、PHP での実装を含むライブラリ" 8 | } -------------------------------------------------------------------------------- /i18n/zh-hant.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Justincheng12345" 5 | ] 6 | }, 7 | "ask-desc": "包含以PHP實現的ASK查詢語言的庫" 8 | } -------------------------------------------------------------------------------- /i18n/he.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Amire80" 5 | ] 6 | }, 7 | "ask-desc": "ספרייה שמממשת את שפת השאילתות Ask ב־PHP" 8 | } -------------------------------------------------------------------------------- /i18n/vi.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Minh Nguyen" 5 | ] 6 | }, 7 | "ask-desc": "Thư viện PHP cho ngôn ngữ truy vấn Ask" 8 | } -------------------------------------------------------------------------------- /i18n/nb.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Jeblad" 5 | ] 6 | }, 7 | "ask-desc": "Bibliotek med PHP-implementasjon av Ask-spørrespråket" 8 | } -------------------------------------------------------------------------------- /i18n/uk.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Ата" 5 | ] 6 | }, 7 | "ask-desc": "Бібліотека, що містить PHP-реалізацію мови запитів Ask" 8 | } -------------------------------------------------------------------------------- /i18n/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Okras" 5 | ] 6 | }, 7 | "ask-desc": "Библиотека, содержащая PHP-реализацию язык запросов Ask" 8 | } -------------------------------------------------------------------------------- /i18n/sv.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "LittleGun" 5 | ] 6 | }, 7 | "ask-desc": "Bibliotek med en PHP implementering av Ask frågespråk" 8 | } -------------------------------------------------------------------------------- /i18n/be-tarask.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Wizardist" 5 | ] 6 | }, 7 | "ask-desc": "Бібліятэка, якая рэалізуе на PHP мову запытаў Ask" 8 | } -------------------------------------------------------------------------------- /i18n/eu.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Theklan" 5 | ] 6 | }, 7 | "ask-desc": "Ask query hizkuntzaren PHP inplementazioa duen liburutegia" 8 | } -------------------------------------------------------------------------------- /i18n/fa.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Reza1615" 5 | ] 6 | }, 7 | "ask-desc": "کتابخانه شامل پیاده‌سازی پی‌اچ‌پی از زبان کوئری پرس و جو است" 8 | } -------------------------------------------------------------------------------- /i18n/mk.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Bjankuloski06" 5 | ] 6 | }, 7 | "ask-desc": "Библиотека што содржи PHP-примена на јазикот за барање Ask" 8 | } -------------------------------------------------------------------------------- /i18n/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Siebrand" 5 | ] 6 | }, 7 | "ask-desc": "Bibliotheek die een PHP-implementatie bevat van de querytaal Ask" 8 | } -------------------------------------------------------------------------------- /i18n/pt-br.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Cainamarques" 5 | ] 6 | }, 7 | "ask-desc": "Biblioteca contendo uma implementação PHP da linguagem Ask" 8 | } -------------------------------------------------------------------------------- /i18n/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Cainamarques" 5 | ] 6 | }, 7 | "ask-desc": "Biblioteca contendo uma implementação PHP da linguagem Ask" 8 | } -------------------------------------------------------------------------------- /i18n/ast.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Xuacu" 5 | ] 6 | }, 7 | "ask-desc": "Biblioteca que contien un encadarmáu en PHP del llinguaxe de consulta Ask" 8 | } -------------------------------------------------------------------------------- /i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Jeroen De Dauw" 5 | ] 6 | }, 7 | "ask-desc": "Library containing a PHP implementation of the Ask query language" 8 | } -------------------------------------------------------------------------------- /i18n/gl.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Toliño" 5 | ] 6 | }, 7 | "ask-desc": "Biblioteca que contén unha integración PHP da linguaxe de consulta Ask" 8 | } -------------------------------------------------------------------------------- /i18n/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Beta16" 5 | ] 6 | }, 7 | "ask-desc": "Libreria contenente un'implementazione PHP del linguaggio di query Ask" 8 | } -------------------------------------------------------------------------------- /i18n/ms.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Anakmalaysia" 5 | ] 6 | }, 7 | "ask-desc": "Perpustakaan yang mengandungi pelaksanaan PHP bahasa pertanyaan Ask" 8 | } -------------------------------------------------------------------------------- /i18n/qqq.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Shirayuki" 5 | ] 6 | }, 7 | "ask-desc": "{{desc|name=Ask|url=http://www.mediawiki.org/wiki/Extension:Ask}}" 8 | } -------------------------------------------------------------------------------- /i18n/dsb.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Michawiki" 5 | ] 6 | }, 7 | "ask-desc": "Biblioteka, kótaraž wopśimujo PHP-implementaciju napšašowańskeje rěcy Ask" 8 | } -------------------------------------------------------------------------------- /i18n/hsb.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Michawiki" 5 | ] 6 | }, 7 | "ask-desc": "Biblioteka, kotraž PHP-implementaciju naprašowanskeje rěče Ask wobsahuje" 8 | } -------------------------------------------------------------------------------- /i18n/oc.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Cedric31" 5 | ] 6 | }, 7 | "ask-desc": "Bibliotèca que conten una implementacion PHP del lengatge de requèsta Ask" 8 | } -------------------------------------------------------------------------------- /i18n/ia.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "McDutchie" 5 | ] 6 | }, 7 | "ask-desc": "Bibliotheca que contine un implementation in PHP del linguage de consulta ASK" 8 | } -------------------------------------------------------------------------------- /i18n/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Fitoschido" 5 | ] 6 | }, 7 | "ask-desc": "Biblioteca que contiene una implementación en PHP del lenguaje de consulta de Ask" 8 | } -------------------------------------------------------------------------------- /i18n/roa-tara.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Joetaras" 5 | ] 6 | }, 7 | "ask-desc": "Libbrerie ca tène 'n'imblemendazione PHP d'a lènghe pe le 'nderrogaziune Ask" 8 | } -------------------------------------------------------------------------------- /i18n/tl.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "AnakngAraw" 5 | ] 6 | }, 7 | "ask-desc": "Aklatan na naglalaman ng isang pagsasakatuparan ng PHP ng wika ng pag-uungkat ng Ask" 8 | } -------------------------------------------------------------------------------- /i18n/bcl.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Geopoet" 5 | ] 6 | }, 7 | "ask-desc": "An Librari na igwang laog na implementasyon kan PHP kan lengguwahe nin Hapot sa kahaputan" 8 | } -------------------------------------------------------------------------------- /i18n/ilo.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Lam-ang" 5 | ] 6 | }, 7 | "ask-desc": "Biblioteka nga aglaon ti maysa a PHP a panangigaretget iti panagsukisok a pagsasao ti Ask" 8 | } -------------------------------------------------------------------------------- /i18n/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Gomoko", 5 | "Tpt" 6 | ] 7 | }, 8 | "ask-desc": "Bibliothèque contenant une implémentation PHP du langage de requête Ask" 9 | } -------------------------------------------------------------------------------- /i18n/ksh.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Purodha" 5 | ] 6 | }, 7 | "ask-desc": "En Biblijoteek met en PHP jeschrevve Projramme, di di Schprooch Ask för et Befroore ömsäze." 8 | } -------------------------------------------------------------------------------- /i18n/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Kghbln", 5 | "Metalhead64" 6 | ] 7 | }, 8 | "ask-desc": "Ergänzt eine Bibliothek, die eine PHP-Implementierung der Abfragesprache „Ask“ enthält" 9 | } -------------------------------------------------------------------------------- /src/Hashable.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | interface Hashable extends \Hashable {} 14 | -------------------------------------------------------------------------------- /src/Comparable.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | interface Comparable extends \Comparable {} 14 | -------------------------------------------------------------------------------- /src/Immutable.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | interface Immutable extends \Immutable {} 15 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | build: true 2 | inherit: true 3 | 4 | before_commands: 5 | - "composer install --prefer-source" 6 | 7 | tools: 8 | php_code_sniffer: true 9 | php_cpd: true 10 | php_cs_fixer: true 11 | php_loc: true 12 | php_mess_detector: true 13 | php_pdepend: true 14 | php_analyzer: true 15 | sensiolabs_security_checker: true 16 | -------------------------------------------------------------------------------- /src/Typeable.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | interface Typeable { 14 | 15 | /** 16 | * Returns a type identifier for the object. 17 | * This identifier does not have to be globally unique, 18 | * though is expected to be unique for objects of the same type. 19 | * 20 | * @since 1.0 21 | * 22 | * @return string 23 | */ 24 | public function getType(); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/Language/Selection/SelectionRequest.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | abstract class SelectionRequest implements \Ask\Comparable, \Ask\Hashable, \Ask\Typeable { 17 | 18 | const TYPE_PROP = 'property'; 19 | const TYPE_SUBJECT = 'subject'; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | - 5.6 8 | - 7 9 | - hhvm 10 | 11 | before_script: 12 | - composer self-update 13 | 14 | script: 15 | - phpunit --coverage-clover build/logs/clover.xml 16 | 17 | after_script: 18 | - composer require satooshi/php-coveralls:dev-master 19 | - php vendor/bin/coveralls -v 20 | 21 | notifications: 22 | email: 23 | recipients: 24 | - jeroendedauw@gmail.com 25 | on_success: change 26 | on_failure: always 27 | irc: 28 | channels: 29 | - "chat.freenode.net#wikidata-feed" 30 | on_success: change 31 | on_failure: always 32 | 33 | cache: 34 | directories: 35 | - $HOME/.composer/cache 36 | -------------------------------------------------------------------------------- /Tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | 12 | 13 | if ( php_sapi_name() !== 'cli' ) { 14 | die( 'Not an entry point' ); 15 | } 16 | 17 | $pwd = getcwd(); 18 | chdir( __DIR__ . '/..' ); 19 | passthru( 'composer update' ); 20 | chdir( $pwd ); 21 | 22 | if ( !is_readable( __DIR__ . '/../vendor/autoload.php' ) ) { 23 | die( 'You need to install this package with Composer before you can run the tests' ); 24 | } 25 | 26 | require_once( __DIR__ . '/../vendor/autoload.php' ); 27 | 28 | require_once( __DIR__ . '/testLoader.php' ); 29 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/Description/AnyValueTest.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class AnyValueTest extends DescriptionTest { 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | protected function getInstances() { 27 | $instances = array(); 28 | 29 | $instances[] = new AnyValue(); 30 | 31 | return $instances; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/Language/Description/Conjunction.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | final class Conjunction extends DescriptionCollection { 19 | 20 | /** 21 | * {@inheritdoc} 22 | * 23 | * @since 1.0 24 | * 25 | * @return string 26 | */ 27 | public function getType() { 28 | return 'conjunction'; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/Language/Description/Disjunction.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | final class Disjunction extends DescriptionCollection { 19 | 20 | /** 21 | * {@inheritdoc} 22 | * 23 | * @since 1.0 24 | * 25 | * @return string 26 | */ 27 | public function getType() { 28 | return 'disjunction'; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/Selection/SubjectSelectionTest.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class SubjectSelectionTest extends SelectionRequestTest { 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | protected function getInstances() { 27 | $instances = array(); 28 | 29 | $instances[] = new SubjectSelection(); 30 | 31 | return $instances; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/Language/Description/Description.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | abstract class Description implements \Ask\Comparable, \Ask\Hashable, \Ask\Typeable { 16 | 17 | /** 18 | * Returns the size of the description. 19 | * 20 | * @since 1.0 21 | * 22 | * @return integer 23 | */ 24 | public abstract function getSize(); 25 | 26 | /** 27 | * Returns the depth of the description. 28 | * 29 | * @since 1.0 30 | * 31 | * @return integer 32 | */ 33 | public abstract function getDepth(); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Ask.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | 13 | if ( defined( 'Ask_VERSION' ) ) { 14 | // Do not initialize more than once. 15 | return 1; 16 | } 17 | 18 | define( 'Ask_VERSION', '1.0.3-alpha' ); 19 | 20 | // Attempt to include the dependencies if one has not been loaded yet. 21 | // This is the path to the autoloader generated by composer in case of a composer install. 22 | if ( !defined( 'DATAVALUES_VERSION' ) && is_readable( __DIR__ . '/vendor/autoload.php' ) ) { 23 | include_once( __DIR__ . '/vendor/autoload.php' ); 24 | } 25 | 26 | if ( defined( 'MEDIAWIKI' ) ) { 27 | call_user_func( function() { 28 | require_once __DIR__ . '/Ask.mw.php'; 29 | } ); 30 | } 31 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | Tests/Phpunit 17 | 18 | 19 | 20 | 21 | src 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/Selection/PropertySelectionTest.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | class PropertySelectionTest extends SelectionRequestTest { 23 | 24 | /** 25 | * {@inheritdoc} 26 | */ 27 | protected function getInstances() { 28 | $instances = array(); 29 | 30 | $instances[] = new PropertySelection( 31 | new StringValue( 'p42' ) 32 | ); 33 | 34 | $instances[] = new PropertySelection( 35 | new StringValue( '_geo' ) 36 | ); 37 | 38 | return $instances; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/Language/Option/SortOptions.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class SortOptions implements \Ask\Immutable { 14 | 15 | /** 16 | * The sort expressions that make up these sort options. 17 | * 18 | * @since 1.0 19 | * 20 | * @var SortExpression[] 21 | */ 22 | protected $expressions; 23 | 24 | /** 25 | * @since 1.0 26 | * 27 | * @param SortExpression[] $expressions 28 | */ 29 | public function __construct( array $expressions ) { 30 | $this->expressions = $expressions; 31 | } 32 | 33 | /** 34 | * Returns the sort expressions that make up these sort options. 35 | * 36 | * @since 1.0 37 | * 38 | * @return SortExpression[] 39 | */ 40 | public function getExpressions() { 41 | return $this->expressions; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Tests/testLoader.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | 12 | spl_autoload_register( function ( $className ) { 13 | $className = ltrim( $className, '\\' ); 14 | $fileName = ''; 15 | $namespace = ''; 16 | 17 | if ( $lastNsPos = strripos( $className, '\\') ) { 18 | $namespace = substr( $className, 0, $lastNsPos ); 19 | $className = substr( $className, $lastNsPos + 1 ); 20 | $fileName = str_replace( '\\', '/', $namespace ) . '/'; 21 | } 22 | 23 | $fileName .= str_replace( '_', '/', $className ) . '.php'; 24 | 25 | $namespaceSegments = explode( '\\', $namespace ); 26 | 27 | if ( count( $namespaceSegments ) > 1 && $namespaceSegments[0] === 'Ask' && $namespaceSegments[1] === 'Tests' ) { 28 | $fileName = substr( $fileName, 4 ); 29 | require_once __DIR__ . '/../' . $fileName; 30 | } 31 | } ); 32 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ask/ask", 3 | "type": "library", 4 | "description": "Library containing a PHP implementation of the Ask query language", 5 | "keywords": [ 6 | "ask", 7 | "wikidata", 8 | "SMW", 9 | "Semantic MediaWiki" 10 | ], 11 | "homepage": "https://github.com/wmde/Ask", 12 | "license": "GPL-2.0+", 13 | "authors": [ 14 | { 15 | "name": "Jeroen De Dauw", 16 | "email": "jeroendedauw@gmail.com", 17 | "homepage": "http://jeroendedauw.com", 18 | "role": "Developer" 19 | }, 20 | { 21 | "name": "Markus Krötzsch", 22 | "homepage": "http://korrekt.org", 23 | "role": "Author of the code this lib is based on" 24 | } 25 | ], 26 | "support": { 27 | "irc": "irc://irc.freenode.net/wikidata" 28 | }, 29 | "require": { 30 | "php": ">=5.3.0", 31 | "data-values/data-values": "~2.0@dev|~1.0|~0.1" 32 | }, 33 | "autoload": { 34 | "files" : [ 35 | "Ask.php" 36 | ], 37 | "psr-4": { 38 | "Ask\\": "src/" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Ask.mw.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | 13 | if ( !defined( 'MEDIAWIKI' ) ) { 14 | die( 'Not an entry point.' ); 15 | } 16 | 17 | $GLOBALS['wgExtensionCredits']['other'][] = array( 18 | 'path' => __FILE__, 19 | 'name' => 'Ask', 20 | 'version' => Ask_VERSION, 21 | 'author' => array( 22 | '[https://www.mediawiki.org/wiki/User:Jeroen_De_Dauw Jeroen De Dauw]', 23 | 24 | // A big part of this library is conceptually based on code from Semantic MediaWiki 1.9 written by Markus 25 | 'Markus Krötzsch', 26 | ), 27 | 'url' => 'https://github.com/wmde/Ask', 28 | 'descriptionmsg' => 'ask-desc', 29 | 'license-name' => 'GPL-2.0+' 30 | ); 31 | 32 | $GLOBALS['wgMessagesDirs']['AskExtension'] = __DIR__ . '/i18n'; 33 | $GLOBALS['wgExtensionMessagesFiles']['AskExtension'] = __DIR__ . '/Ask.i18n.php'; 34 | -------------------------------------------------------------------------------- /src/Language/Selection/SubjectSelection.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | final class SubjectSelection extends SelectionRequest implements \Ask\Immutable { 14 | 15 | /** 16 | * @see Typeable::getType 17 | * 18 | * @since 1.0 19 | * 20 | * @return string 21 | */ 22 | public function getType() { 23 | return SelectionRequest::TYPE_SUBJECT; 24 | } 25 | 26 | /** 27 | * @see Comparable::equals 28 | * 29 | * @since 1.0 30 | * 31 | * @param mixed $mixed 32 | * 33 | * @return boolean 34 | */ 35 | public function equals( $mixed ) { 36 | return $mixed instanceof SubjectSelection; 37 | } 38 | 39 | /** 40 | * @see Hashable::getHash 41 | * 42 | * @since 1.0 43 | * 44 | * @return string 45 | */ 46 | public function getHash() { 47 | return sha1( $this->getType() ); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/Language/Option/SortExpression.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | abstract class SortExpression implements \Ask\Immutable, \Ask\Typeable { 16 | 17 | const PROPERTY_VALUE = 'propertyValue'; 18 | 19 | const DIRECTION_ASCENDING = 'asc'; 20 | const DIRECTION_DESCENDING = 'desc'; 21 | 22 | /** 23 | * The sort direction. 24 | * This is one of the SortExpression::DIRECTION_ constants. 25 | * 26 | * @var string|null 27 | */ 28 | protected $direction = null; 29 | 30 | /** 31 | * Returns the sort direction. 32 | * This is one of the SortExpression::DIRECTION_ constants. 33 | * 34 | * @since 1.0 35 | * 36 | * @return string 37 | */ 38 | public function getDirection() { 39 | assert( $this->direction !== null ); 40 | return $this->direction; 41 | } 42 | 43 | protected function assertIsDirection( $direction ) { 44 | if ( !is_string( $direction ) || !in_array( $direction, array( self::DIRECTION_ASCENDING, self::DIRECTION_DESCENDING ) ) ) { 45 | throw new InvalidArgumentException( '$direction needs to be one of the direction constants' ); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/Option/SortExpressionTest.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | abstract class SortExpressionTest extends \Ask\Tests\Phpunit\AskTestCase { 19 | 20 | /** 21 | * @since 1.0 22 | * 23 | * @return SortExpression[] 24 | */ 25 | protected abstract function getInstances(); 26 | 27 | /** 28 | * @since 1.0 29 | * 30 | * @return SortExpression[][] 31 | */ 32 | public function instanceProvider() { 33 | return $this->arrayWrap( $this->getInstances() ); 34 | } 35 | 36 | /** 37 | * @dataProvider instanceProvider 38 | * 39 | * @since 1.0 40 | * 41 | * @param SortExpression $expression 42 | */ 43 | public function testReturnValueOfGetDirection( SortExpression $expression ) { 44 | $direction = $expression->getDirection(); 45 | 46 | $this->assertInternalType( 'string', $direction ); 47 | $this->assertTrue( 48 | in_array( $direction, array( SortExpression::DIRECTION_ASCENDING, SortExpression::DIRECTION_DESCENDING ) ), 49 | 'Sort direction is one of the known values' 50 | ); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/Language/Description/AnyValue.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | final class AnyValue extends Description implements \Ask\Immutable { 18 | 19 | /** 20 | * {@inheritdoc} 21 | * 22 | * @since 1.0 23 | * 24 | * @return integer 25 | */ 26 | public function getSize() { 27 | return 0; 28 | } 29 | 30 | /** 31 | * {@inheritdoc} 32 | * 33 | * @since 1.0 34 | * 35 | * @return integer 36 | */ 37 | public function getDepth() { 38 | return 0; 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | * 44 | * @since 1.0 45 | * 46 | * @return string 47 | */ 48 | public function getType() { 49 | return 'anyValue'; 50 | } 51 | 52 | /** 53 | * @see Comparable::equals 54 | * 55 | * @since 1.0 56 | * 57 | * @param mixed $mixed 58 | * 59 | * @return boolean 60 | */ 61 | public function equals( $mixed ) { 62 | return $mixed instanceof AnyValue; 63 | } 64 | 65 | /** 66 | * @see Hashable::getHash 67 | * 68 | * @since 1.0 69 | * 70 | * @return string 71 | */ 72 | public function getHash() { 73 | return sha1( $this->getType() ); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Tests/Phpunit/AskTestCase.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | abstract class AskTestCase extends \PHPUnit_Framework_TestCase { 17 | 18 | /** 19 | * Utility method taking an array of elements and wrapping 20 | * each element in it's own array. Useful for data providers 21 | * that only return a single argument. 22 | * 23 | * @since 1.0 24 | * 25 | * @param array $elements 26 | * 27 | * @return array 28 | */ 29 | protected function arrayWrap( array $elements ) { 30 | return array_map( 31 | function( $element ) { 32 | return array( $element ); 33 | }, 34 | $elements 35 | ); 36 | } 37 | 38 | protected function assertPrimitiveStructure( $value ) { 39 | if ( is_array( $value ) || is_object( $value ) ) { 40 | // TODO: would be good if we could reject objects that are not simple maps 41 | $value = (array)$value; 42 | 43 | if ( empty( $value ) ) { 44 | $this->assertTrue( true ); 45 | } 46 | 47 | foreach ( $value as $subValue ) { 48 | $this->assertPrimitiveStructure( $subValue ); 49 | } 50 | } 51 | else { 52 | $this->assertFalse( is_resource( $value ), 'Value should not be a resource' ); 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/Language/Option/PropertyValueSortExpression.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class PropertyValueSortExpression extends SortExpression { 17 | 18 | /** 19 | * The property value to sort by. 20 | * 21 | * @since 1.0 22 | * 23 | * @var DataValue 24 | */ 25 | protected $property; 26 | 27 | /** 28 | * @since 1.0 29 | * 30 | * @param DataValue $propertyId 31 | * @param string $direction One of the SortExpression::DIRECTION_ constants 32 | * 33 | * @throws InvalidArgumentException 34 | */ 35 | public function __construct( DataValue $propertyId, $direction ) { 36 | $this->property = $propertyId; 37 | 38 | $this->assertIsDirection( $direction ); 39 | 40 | $this->direction = $direction; 41 | } 42 | 43 | /** 44 | * Returns the property value to sort by. 45 | * 46 | * @since 1.0 47 | * 48 | * @return DataValue 49 | */ 50 | public function getPropertyId() { 51 | return $this->property; 52 | } 53 | 54 | /** 55 | * {@inheritdoc} 56 | * 57 | * @since 1.0 58 | * 59 | * @return string 60 | */ 61 | public function getType() { 62 | return SortExpression::PROPERTY_VALUE; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Ask.i18n.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class SortOptionsTest extends AskTestCase { 26 | 27 | /** 28 | * @dataProvider sortExpressionListProvider 29 | */ 30 | public function testGetExpressions( array $expressions ) { 31 | $options = new SortOptions( $expressions ); 32 | 33 | $this->assertEquals( $expressions, $options->getExpressions() ); 34 | } 35 | 36 | public function sortExpressionListProvider() { 37 | $argLists = array(); 38 | 39 | $argLists[] = array( array() ); 40 | 41 | $argLists[] = array( array( 42 | new PropertyValueSortExpression( 43 | new StringValue( 'foo' ), 44 | SortExpression::DIRECTION_ASCENDING 45 | ) 46 | ) ); 47 | 48 | $argLists[] = array( array( 49 | new PropertyValueSortExpression( 50 | new StringValue( 'foo' ), 51 | SortExpression::DIRECTION_ASCENDING 52 | ), 53 | new PropertyValueSortExpression( 54 | new StringValue( 'bar' ), 55 | SortExpression::DIRECTION_DESCENDING 56 | ), 57 | ) ); 58 | 59 | return $argLists; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/Language/Option/QueryOptions.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class QueryOptions implements \Ask\Immutable { 14 | 15 | /** 16 | * The query limit. At most this many results will be selected. 17 | * 18 | * @since 1.0 19 | * 20 | * @var int 21 | */ 22 | protected $limit; 23 | 24 | /** 25 | * The query offset. The first this many matching results will be skipped. 26 | * 27 | * @since 1.0 28 | * 29 | * @var int 30 | */ 31 | protected $offset; 32 | 33 | /** 34 | * The query sort. 35 | * 36 | * @since 1.0 37 | * 38 | * @var SortOptions 39 | */ 40 | protected $sort; 41 | 42 | /** 43 | * @since 1.0 44 | * 45 | * @param int $limit 46 | * @param int $offset 47 | * @param SortOptions|null $sort 48 | */ 49 | public function __construct( $limit, $offset, SortOptions $sort = null ) { 50 | $this->limit = $limit; 51 | $this->offset = $offset; 52 | $this->sort = $sort === null ? new SortOptions( array() ) : $sort; 53 | } 54 | 55 | /** 56 | * Returns the query limit. 57 | * 58 | * @since 1.0 59 | * 60 | * @return int 61 | */ 62 | public function getLimit() { 63 | return $this->limit; 64 | } 65 | 66 | /** 67 | * Returns the query offset. 68 | * 69 | * @since 1.0 70 | * 71 | * @return int 72 | */ 73 | public function getOffset() { 74 | return $this->offset; 75 | } 76 | 77 | /** 78 | * Returns the query sort options. 79 | * 80 | * @since 1.0 81 | * 82 | * @return SortOptions 83 | */ 84 | public function getSort() { 85 | return $this->sort; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/Language/Selection/PropertySelection.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class PropertySelection extends SelectionRequest implements \Ask\Immutable { 16 | 17 | /** 18 | * The property for which to select values. 19 | * 20 | * @since 1.0 21 | * 22 | * @var DataValue 23 | */ 24 | protected $propertyId; 25 | 26 | /** 27 | * @since 1.0 28 | * 29 | * @param DataValue $propertyId 30 | */ 31 | public function __construct( DataValue $propertyId ) { 32 | $this->propertyId = $propertyId; 33 | } 34 | 35 | /** 36 | * @see Typeable::getType 37 | * 38 | * @since 1.0 39 | * 40 | * @return string 41 | */ 42 | public function getType() { 43 | return SelectionRequest::TYPE_PROP; 44 | } 45 | 46 | /** 47 | * Returns the print request's property. 48 | * 49 | * @since 1.0 50 | * 51 | * @return DataValue 52 | */ 53 | public function getPropertyId() { 54 | return $this->propertyId; 55 | } 56 | 57 | /** 58 | * @see Comparable::equals 59 | * 60 | * @since 1.0 61 | * 62 | * @param mixed $mixed 63 | * 64 | * @return boolean 65 | */ 66 | public function equals( $mixed ) { 67 | return $mixed instanceof PropertySelection 68 | && $this->propertyId->equals( $mixed->getPropertyId() ); 69 | } 70 | 71 | /** 72 | * @see Hashable::getHash 73 | * 74 | * @since 1.0 75 | * 76 | * @return string 77 | */ 78 | public function getHash() { 79 | return sha1( $this->getType() . $this->propertyId->getHash() ); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/Option/QueryOptionsTest.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class QueryOptionsTest extends AskTestCase { 24 | 25 | /** 26 | * @since 1.0 27 | * 28 | * @return QueryOptions[] 29 | */ 30 | protected function getInstances() { 31 | $instances = array(); 32 | 33 | $instances[] = new QueryOptions( 34 | 100, 35 | 0 36 | ); 37 | 38 | $instances[] = new QueryOptions( 39 | 5, 40 | 100 41 | ); 42 | 43 | $instances[] = new QueryOptions( 44 | 9000, 45 | 42, 46 | new SortOptions( array() ) 47 | ); 48 | 49 | return $instances; 50 | } 51 | 52 | /** 53 | * @since 1.0 54 | * 55 | * @return QueryOptions[][] 56 | */ 57 | public function instanceProvider() { 58 | return $this->arrayWrap( $this->getInstances() ); 59 | } 60 | 61 | /** 62 | * @dataProvider constructorArgumentProvider 63 | */ 64 | public function testConstructSetsFields( $limit, $offset, SortOptions $sort ) { 65 | $queryOptions = new QueryOptions( $limit, $offset, $sort ); 66 | 67 | $this->assertEquals( $limit, $queryOptions->getLimit() ); 68 | $this->assertEquals( $offset, $queryOptions->getOffset() ); 69 | $this->assertEquals( $sort, $queryOptions->getSort() ); 70 | } 71 | 72 | public function constructorArgumentProvider() { 73 | $argLists = array(); 74 | 75 | $argLists[] = array( 1, 2, new SortOptions( array() ) ); 76 | 77 | $argLists[] = array( 100, 0, new SortOptions( array() ) ); 78 | 79 | return $argLists; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/Option/PropertyValueSortExpressionTest.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | class PropertyValueSortExpressionTest extends SortExpressionTest { 25 | 26 | public function testCanConstruct() { 27 | new PropertyValueSortExpression( 28 | new StringValue( 'foo' ), 29 | SortExpression::DIRECTION_ASCENDING 30 | ); 31 | 32 | $this->assertTrue( true ); 33 | } 34 | 35 | public function testGetPropertyId() { 36 | $propertyId = new StringValue( 'foo' ); 37 | 38 | $sortExpression = new PropertyValueSortExpression( 39 | $propertyId, 40 | SortExpression::DIRECTION_ASCENDING 41 | ); 42 | 43 | $this->assertEquals( $propertyId, $sortExpression->getPropertyId() ); 44 | } 45 | 46 | /** 47 | * @see SortExpressionTest::getInstances 48 | * 49 | * @since 1.0 50 | * 51 | * @return SortExpression[] 52 | */ 53 | protected function getInstances() { 54 | $instances = array(); 55 | 56 | $instances[] = new PropertyValueSortExpression( 57 | new StringValue( 'foo' ), 58 | SortExpression::DIRECTION_ASCENDING 59 | ); 60 | 61 | $instances[] = new PropertyValueSortExpression( 62 | new StringValue( 'foo' ), 63 | SortExpression::DIRECTION_DESCENDING 64 | ); 65 | 66 | return $instances; 67 | } 68 | 69 | /** 70 | * @dataProvider invalidSortDirectionProvider 71 | */ 72 | public function testCannotConstructWithInvalidSortDirection( $invalidSortDirection ) { 73 | $this->setExpectedException( 'InvalidArgumentException' ); 74 | 75 | new PropertyValueSortExpression( 76 | new StringValue( 'foo' ), 77 | $invalidSortDirection 78 | ); 79 | } 80 | 81 | public function invalidSortDirectionProvider() { 82 | return array( 83 | array( null ), 84 | array( array() ), 85 | array( true ), 86 | array( 4.2 ), 87 | array( 'foo' ), 88 | ); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/Selection/SelectionRequestTest.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | abstract class SelectionRequestTest extends \Ask\Tests\Phpunit\AskTestCase { 19 | 20 | /** 21 | * @since 1.0 22 | * 23 | * @return SelectionRequest[] 24 | */ 25 | protected abstract function getInstances(); 26 | 27 | /** 28 | * @since 1.0 29 | * 30 | * @return SelectionRequest[][] 31 | */ 32 | public function instanceProvider() { 33 | return $this->arrayWrap( $this->getInstances() ); 34 | } 35 | 36 | /** 37 | * @dataProvider instanceProvider 38 | * 39 | * @since 1.0 40 | * 41 | * @param SelectionRequest $request 42 | */ 43 | public function testReturnTypeOfGetType( SelectionRequest $request ) { 44 | $this->assertInternalType( 'string', $request->getType() ); 45 | } 46 | 47 | /** 48 | * @dataProvider instanceProvider 49 | * 50 | * @since 1.0 51 | * 52 | * @param SelectionRequest $object 53 | */ 54 | public function testComparableSelfIsEqual( SelectionRequest $object ) { 55 | $this->assertTrue( $object->equals( $object ), 'Description is equal to itself' ); 56 | } 57 | 58 | /** 59 | * @dataProvider instanceProvider 60 | * 61 | * @since 1.0 62 | * 63 | * @param SelectionRequest $object 64 | */ 65 | public function testComparableNotEqual( SelectionRequest $object ) { 66 | $this->assertFalse( $object->equals( '~[,,_,,]:3' ), 'Description not equal to string' ); 67 | $this->assertFalse( $object->equals( new \stdClass() ), 'Description not equal to empty object' ); 68 | } 69 | 70 | /** 71 | * @dataProvider instanceProvider 72 | * 73 | * @since 1.0 74 | * 75 | * @param SelectionRequest $object 76 | */ 77 | public function testGetHashReturnType( SelectionRequest $object ) { 78 | $this->assertInternalType( 'string', $object->getHash() ); 79 | } 80 | 81 | /** 82 | * @dataProvider instanceProvider 83 | * 84 | * @since 1.0 85 | * 86 | * @param SelectionRequest $object 87 | */ 88 | public function testGetHashStability( SelectionRequest $object ) { 89 | $this->assertEquals( $object->getHash(), $object->getHash() ); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/Language/Query.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class Query implements \Ask\Immutable { 18 | 19 | const OPT_LIMIT = 'limit'; 20 | const OPT_OFFSET = 'offset'; 21 | const OPT_SORT = 'sort'; 22 | 23 | /** 24 | * The query's description. 25 | * This is the selection criterion of the query that determines which entities match. 26 | * It is conceptually similar to the WHERE clause in an SQL query. 27 | * 28 | * @since 1.0 29 | * 30 | * @var Description 31 | */ 32 | protected $description; 33 | 34 | /** 35 | * The query's selection requests. 36 | * These determine which information should be selected from the matching entities. 37 | * It is conceptually similar to the SELECT clause in an SQL query. 38 | * 39 | * @since 1.0 40 | * 41 | * @var SelectionRequest[] 42 | */ 43 | protected $selectionRequests; 44 | 45 | /** 46 | * The query's options. 47 | * 48 | * @since 1.0 49 | * 50 | * @var QueryOptions 51 | */ 52 | protected $options; 53 | 54 | /** 55 | * @since 1.0 56 | * 57 | * @param Description $description 58 | * @param SelectionRequest[] $selectionRequests 59 | * @param QueryOptions $options 60 | */ 61 | public function __construct( Description $description, array $selectionRequests, QueryOptions $options ) { 62 | $this->description = $description; 63 | $this->selectionRequests = $selectionRequests; 64 | $this->options = $options; 65 | } 66 | 67 | /** 68 | * Returns the query description. 69 | * 70 | * @since 1.0 71 | * 72 | * @return Description 73 | */ 74 | public function getDescription() { 75 | return $this->description; 76 | } 77 | 78 | /** 79 | * Returns the query's selection requests. 80 | * 81 | * @since 1.0 82 | * 83 | * @return SelectionRequest[] 84 | */ 85 | public function getSelectionRequests() { 86 | return $this->selectionRequests; 87 | } 88 | 89 | /** 90 | * Returns the query's options. 91 | * 92 | * @since 1.0 93 | * 94 | * @return QueryOptions 95 | */ 96 | public function getOptions() { 97 | return $this->options; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/Description/ConjunctionTest.php: -------------------------------------------------------------------------------- 1 | 27 | */ 28 | class ConjunctionTest extends DescriptionCollectionTest { 29 | 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | protected function getInstances() { 34 | $instances = array(); 35 | 36 | $instances[] = new Conjunction( array() ); 37 | $instances[] = new Conjunction( array( new Conjunction( array() ) ) ); 38 | $instances[] = new Conjunction( array( new Disjunction( array() ), new Disjunction( array() ) ) ); 39 | $instances[] = new Conjunction( array( new AnyValue() ) ); 40 | $instances[] = new Conjunction( array( new ValueDescription( new StringValue( 'ohi' ) ) ) ); 41 | 42 | foreach ( $this->descriptionsProvider() as $argList ) { 43 | $instances[] = new Conjunction( $argList[0] ); 44 | } 45 | 46 | return $instances; 47 | } 48 | 49 | /** 50 | * @dataProvider instanceProvider 51 | * 52 | * @since 1.0 53 | * 54 | * @param Conjunction $description 55 | */ 56 | public function testGetDescriptions( Conjunction $description ) { 57 | $descriptions = $description->getDescriptions(); 58 | 59 | $this->assertInternalType( 'array', $descriptions ); 60 | 61 | foreach ( $descriptions as $subInstance ) { 62 | $this->assertInstanceOf( 'Ask\Language\Description\Description', $subInstance ); 63 | } 64 | 65 | $newInstance = new Disjunction( $descriptions ); 66 | 67 | $this->assertEquals( $descriptions, $newInstance->getDescriptions(), 'Descriptions are returned as it was passed to the constructor' ); 68 | } 69 | 70 | /** 71 | * @see DescriptionCollectionTest::newFromDescriptions 72 | * 73 | * @param Description[] $descriptions 74 | * 75 | * @return DescriptionCollection 76 | */ 77 | protected function newFromDescriptions( array $descriptions ) { 78 | return new Conjunction( $descriptions ); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/Description/DisjunctionTest.php: -------------------------------------------------------------------------------- 1 | 27 | */ 28 | class DisjunctionTest extends DescriptionCollectionTest { 29 | 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | protected function getInstances() { 34 | $instances = array(); 35 | 36 | $instances[] = new Disjunction( array() ); 37 | $instances[] = new Disjunction( array( new Conjunction( array() ) ) ); 38 | $instances[] = new Disjunction( array( new Disjunction( array() ), new Disjunction( array() ) ) ); 39 | $instances[] = new Disjunction( array( new AnyValue() ) ); 40 | $instances[] = new Disjunction( array( new ValueDescription( new StringValue( 'ohi' ) ) ) ); 41 | 42 | foreach ( $this->descriptionsProvider() as $argList ) { 43 | $instances[] = new Disjunction( $argList[0] ); 44 | } 45 | 46 | return $instances; 47 | } 48 | 49 | /** 50 | * @dataProvider instanceProvider 51 | * 52 | * @since 1.0 53 | * 54 | * @param Disjunction $description 55 | */ 56 | public function testGetDescriptions( Disjunction $description ) { 57 | $descriptions = $description->getDescriptions(); 58 | 59 | $this->assertInternalType( 'array', $descriptions ); 60 | 61 | foreach ( $descriptions as $subInstance ) { 62 | $this->assertInstanceOf( 'Ask\Language\Description\Description', $subInstance ); 63 | } 64 | 65 | $newInstance = new Disjunction( $descriptions ); 66 | 67 | $this->assertEquals( $descriptions, $newInstance->getDescriptions(), 'Descriptions are returned as it was passed to the constructor' ); 68 | } 69 | 70 | /** 71 | * @see DescriptionCollectionTest::newFromDescriptions 72 | * 73 | * @param Description[] $descriptions 74 | * 75 | * @return DescriptionCollection 76 | */ 77 | protected function newFromDescriptions( array $descriptions ) { 78 | return new Disjunction( $descriptions ); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/QueryTest.php: -------------------------------------------------------------------------------- 1 | 26 | */ 27 | class QueryTest extends \Ask\Tests\Phpunit\AskTestCase { 28 | 29 | public function descriptionProvider() { 30 | $descriptions = array(); 31 | 32 | $descriptions[] = new AnyValue(); 33 | $descriptions[] = new Conjunction( array() ); 34 | $descriptions[] = new Disjunction( array() ); 35 | 36 | return $this->arrayWrap( $descriptions ); 37 | } 38 | 39 | /** 40 | * @dataProvider descriptionProvider 41 | * 42 | * @param Description $description 43 | */ 44 | public function testGetDescriptions( Description $description ) { 45 | $query = new Query( $description, array(), new QueryOptions( 100, 0 ) ); 46 | 47 | $obtainedDescription = $query->getDescription(); 48 | 49 | $this->assertInstanceOf( '\Ask\Language\Description\Description', $obtainedDescription ); 50 | 51 | $this->assertEquals( $description, $obtainedDescription ); 52 | } 53 | 54 | public function selectionRequestsProvider() { 55 | $requestsLists = array(); 56 | 57 | $requestsLists[] = array( 58 | new PropertySelection( new StringValue( 'q42' ) ), 59 | new PropertySelection( new StringValue( '_geo' ) ), 60 | ); 61 | 62 | return $this->arrayWrap( $requestsLists ); 63 | } 64 | 65 | /** 66 | * @dataProvider selectionRequestsProvider 67 | * 68 | * @param array $selectionRequests 69 | */ 70 | public function testGetSelectionRequests( array $selectionRequests ) { 71 | $query = new Query( new AnyValue(), $selectionRequests, new QueryOptions( 100, 0 ) ); 72 | 73 | $obtainedRequests = $query->getSelectionRequests(); 74 | 75 | $this->assertInternalType( 'array', $obtainedRequests ); 76 | $this->assertContainsOnlyInstancesOf( '\Ask\Language\Selection\SelectionRequest', $obtainedRequests ); 77 | 78 | $this->assertEquals( $selectionRequests, $obtainedRequests ); 79 | } 80 | 81 | public function instanceProvider() { 82 | $instances = array(); 83 | 84 | $instances[] = new Query( 85 | new AnyValue(), 86 | array( 87 | new PropertySelection( new StringValue( 'q42' ) ), 88 | new PropertySelection( new StringValue( '_geo' ) ), 89 | ), 90 | new QueryOptions( 100, 0 ) 91 | ); 92 | 93 | return $this->arrayWrap( $instances ); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/Description/ValueDescriptionTest.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class ValueDescriptionTest extends DescriptionTest { 24 | 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | protected function getInstances() { 29 | $instances = array(); 30 | 31 | $values = array( 32 | new StringValue( 'ohi there' ), 33 | new NumberValue( 4.2 ), 34 | ); 35 | 36 | $comparators = array( 37 | ValueDescription::COMP_EQUAL, 38 | ValueDescription::COMP_LEQ, 39 | ValueDescription::COMP_GEQ, 40 | ValueDescription::COMP_NEQ, 41 | ValueDescription::COMP_LIKE, 42 | ValueDescription::COMP_NLIKE, 43 | ValueDescription::COMP_LESS, 44 | ValueDescription::COMP_GREATER, 45 | ); 46 | 47 | foreach ( $values as $value ) { 48 | foreach ( $comparators as $comparator ) { 49 | $instances[] = new ValueDescription( $value, $comparator ); 50 | } 51 | } 52 | 53 | return $instances; 54 | } 55 | 56 | /** 57 | * @dataProvider instanceProvider 58 | * 59 | * @since 1.0 60 | * 61 | * @param ValueDescription $description 62 | */ 63 | public function testGetValue( ValueDescription $description ) { 64 | $value = $description->getValue(); 65 | 66 | $this->assertInstanceOf( 'DataValues\DataValue', $value ); 67 | 68 | $newInstance = new ValueDescription( $value ); 69 | 70 | $this->assertTrue( $value->equals( $newInstance->getValue() ), 'Value is returned as it was passed to the constructor' ); 71 | } 72 | 73 | /** 74 | * @dataProvider instanceProvider 75 | * 76 | * @since 1.0 77 | * 78 | * @param ValueDescription $description 79 | */ 80 | public function testGetComparator( ValueDescription $description ) { 81 | $comparator = $description->getComparator(); 82 | 83 | $this->assertInternalType( 'integer', $comparator ); 84 | 85 | $newInstance = new ValueDescription( $description->getValue(), $comparator ); 86 | 87 | $this->assertEquals( $comparator, $newInstance->getComparator(), 'Comparator is returned as it was passed to the constructor' ); 88 | } 89 | 90 | /** 91 | * @dataProvider invalidComparatorProvider 92 | */ 93 | public function testCannotConstructWithInvalidComparator( $invalidComparator ) { 94 | $this->setExpectedException( 'InvalidArgumentException' ); 95 | new ValueDescription( new StringValue( 'foo' ), $invalidComparator ); 96 | } 97 | 98 | public function invalidComparatorProvider() { 99 | return array( 100 | array( null ), 101 | array( array() ), 102 | array( 4.2 ), 103 | array( true ), 104 | array( 'foo' ), 105 | array( '' ), 106 | ); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/Description/SomePropertyTest.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | class SomePropertyTest extends DescriptionTest { 25 | 26 | /** 27 | * {@inheritdoc} 28 | */ 29 | protected function getInstances() { 30 | $instances = array(); 31 | 32 | $instances[] = new SomeProperty( new StringValue( '_geo' ), new AnyValue() ); 33 | $instances[] = new SomeProperty( new StringValue( 'p42' ), new Conjunction( array() ) ); 34 | $instances[] = new SomeProperty( new StringValue( 'foo' ), new AnyValue(), true ); 35 | $instances[] = new SomeProperty( new StringValue( '~=[,,_,,]:3' ), new AnyValue(), false ); 36 | 37 | return $instances; 38 | } 39 | 40 | /** 41 | * @dataProvider instanceProvider 42 | * 43 | * @since 1.0 44 | * 45 | * @param SomeProperty $description 46 | */ 47 | public function testGetDescription( SomeProperty $description ) { 48 | $subDescription = $description->getSubDescription(); 49 | 50 | $this->assertInstanceOf( 'Ask\Language\Description\Description', $subDescription ); 51 | 52 | $newInstance = new SomeProperty( $description->getPropertyId(), $subDescription ); 53 | 54 | $this->assertEquals( 55 | $subDescription, 56 | $newInstance->getSubDescription(), 57 | 'Description is returned as it was passed to the constructor' 58 | ); 59 | } 60 | 61 | /** 62 | * @dataProvider instanceProvider 63 | * 64 | * @since 1.0 65 | * 66 | * @param SomeProperty $description 67 | */ 68 | public function testGetProperty( SomeProperty $description ) { 69 | $property = $description->getPropertyId(); 70 | 71 | $this->assertInstanceOf( '\DataValues\DataValue', $property ); 72 | 73 | $newInstance = new SomeProperty( $property, $description->getSubDescription() ); 74 | 75 | $this->assertEquals( 76 | $property, 77 | $newInstance->getPropertyId(), 78 | 'Property is returned as it was passed to the constructor' 79 | ); 80 | } 81 | 82 | /** 83 | * @dataProvider instanceProvider 84 | * 85 | * @since 1.0 86 | * 87 | * @param SomeProperty $description 88 | */ 89 | public function testIsSubProperty( SomeProperty $description ) { 90 | $isSubProperty = $description->isSubProperty(); 91 | 92 | $this->assertInternalType( 'boolean', $isSubProperty ); 93 | 94 | $newInstance = new SomeProperty( $description->getPropertyId(), $description->getSubDescription(), $isSubProperty ); 95 | 96 | $this->assertEquals( 97 | $isSubProperty, 98 | $newInstance->isSubProperty(), 99 | 'Is sub property is returned as it was passed to the constructor' 100 | ); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/Description/DescriptionTest.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | abstract class DescriptionTest extends \Ask\Tests\Phpunit\AskTestCase { 19 | 20 | /** 21 | * @since 1.0 22 | * 23 | * @return Description[] 24 | */ 25 | protected abstract function getInstances(); 26 | 27 | /** 28 | * @since 1.0 29 | * 30 | * @return Description[][] 31 | */ 32 | public function instanceProvider() { 33 | return $this->arrayWrap( $this->getInstances() ); 34 | } 35 | 36 | /** 37 | * @dataProvider instanceProvider 38 | * 39 | * @since 1.0 40 | * 41 | * @param Description $description 42 | */ 43 | public function testReturnTypeOfGetSize( Description $description ) { 44 | $size = $description->getSize(); 45 | 46 | $this->assertInternalType( 'integer', $size ); 47 | $this->assertGreaterThanOrEqual( 0, $size ); 48 | $this->assertEquals( $size, $description->getSize() ); 49 | } 50 | 51 | /** 52 | * @dataProvider instanceProvider 53 | * 54 | * @since 1.0 55 | * 56 | * @param Description $description 57 | */ 58 | public function testReturnTypeOfGetDepth( Description $description ) { 59 | $depth = $description->getDepth(); 60 | 61 | $this->assertInternalType( 'integer', $depth ); 62 | $this->assertGreaterThanOrEqual( 0, $depth ); 63 | $this->assertEquals( $depth, $description->getDepth() ); 64 | } 65 | 66 | /** 67 | * @dataProvider instanceProvider 68 | * 69 | * @since 1.0 70 | * 71 | * @param Description $description 72 | */ 73 | public function testComparableSelfIsEqual( Description $description ) { 74 | $this->assertTrue( $description->equals( $description ), 'Description is equal to itself' ); 75 | } 76 | 77 | /** 78 | * @dataProvider instanceProvider 79 | * 80 | * @since 1.0 81 | * 82 | * @param Description $description 83 | */ 84 | public function testComparableNotEqual( Description $description ) { 85 | $this->assertFalse( $description->equals( '~[,,_,,]:3' ), 'Description not equal to string' ); 86 | $this->assertFalse( $description->equals( new \stdClass() ), 'Description not equal to empty object' ); 87 | $this->assertFalse( $description->equals( new FooDescription() ), 'Description not equal to a FooDescription' ); 88 | } 89 | 90 | /** 91 | * @dataProvider instanceProvider 92 | * 93 | * @since 1.0 94 | * 95 | * @param Description $description 96 | */ 97 | public function testGetHashReturnType( Description $description ) { 98 | $this->assertInternalType( 'string', $description->getHash() ); 99 | } 100 | 101 | /** 102 | * @dataProvider instanceProvider 103 | * 104 | * @since 1.0 105 | * 106 | * @param Description $description 107 | */ 108 | public function testGetHashStability( Description $description ) { 109 | $this->assertEquals( $description->getHash(), $description->getHash() ); 110 | } 111 | 112 | } 113 | 114 | class FooDescription extends \Ask\Language\Description\DescriptionCollection { 115 | 116 | public function __construct() { 117 | parent::__construct( array() ); 118 | } 119 | 120 | public function getType() { 121 | return 'foo'; 122 | } 123 | 124 | public function equals( $t ) { 125 | return false; 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /Tests/Phpunit/Language/Description/DescriptionCollectionTest.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | abstract class DescriptionCollectionTest extends DescriptionTest { 25 | 26 | public function descriptionsProvider() { 27 | $descriptionLists = array(); 28 | 29 | $descriptionLists[] = array(); 30 | 31 | $descriptionLists[] = array( 32 | new AnyValue(), 33 | new ValueDescription( new StringValue( 'nyan nyan' ) ) 34 | ); 35 | 36 | $descriptionList = array(); 37 | $numbers = range( 0, 100 ); 38 | shuffle( $numbers ); 39 | 40 | foreach ( $numbers as $number ) { 41 | $descriptionList[] = new ValueDescription( new NumberValue( $number ) ); 42 | } 43 | 44 | $descriptionLists[] = $descriptionList; 45 | 46 | return $this->arrayWrap( $descriptionLists ); 47 | } 48 | 49 | /** 50 | * @param Description[] $descriptions 51 | * 52 | * @return DescriptionCollection 53 | */ 54 | protected abstract function newFromDescriptions( array $descriptions ); 55 | 56 | /** 57 | * @dataProvider descriptionsProvider 58 | * 59 | * @param Description[] $descriptions 60 | */ 61 | public function testEqualsOrderInsensitivity( array $descriptions ) { 62 | $sameDescriptions = $descriptions; 63 | shuffle( $sameDescriptions ); 64 | 65 | $description = $this->newFromDescriptions( $descriptions ); 66 | $sameDescription = $this->newFromDescriptions( $sameDescriptions ); 67 | 68 | $message = 'Two collection descriptions with same sub descriptions should be equal'; 69 | 70 | $this->assertTrue( $description->equals( $sameDescription ), $message ); 71 | $this->assertTrue( $sameDescription->equals( $description ), $message ); 72 | } 73 | 74 | /** 75 | * @dataProvider descriptionsProvider 76 | * 77 | * @param Description[] $descriptions 78 | */ 79 | public function testGetHashOrderInsensitivity( array $descriptions ) { 80 | $sameDescriptions = $descriptions; 81 | shuffle( $sameDescriptions ); 82 | 83 | $description = $this->newFromDescriptions( $descriptions ); 84 | $sameDescription = $this->newFromDescriptions( $sameDescriptions ); 85 | 86 | $this->assertEquals( $description->getHash(), $sameDescription->getHash() ); 87 | } 88 | 89 | /** 90 | * @dataProvider descriptionsProvider 91 | * 92 | * @param Description[] $descriptions 93 | */ 94 | public function testDifferentSizeNotEquals( array $descriptions ) { 95 | $firstDescription = $this->newFromDescriptions( $descriptions ); 96 | 97 | $descriptions[] = new AnyValue(); 98 | 99 | $secondDescription = $this->newFromDescriptions( $descriptions ); 100 | 101 | $this->assertFalse( $firstDescription->equals( $secondDescription ) ); 102 | } 103 | 104 | public function testDifferentContainedDescriptionCausesInequality() { 105 | $firstDescription = $this->newFromDescriptions( array( new AnyValue() ) ); 106 | $secondDescription = $this->newFromDescriptions( array( new Conjunction( array() ) ) ); 107 | 108 | $this->assertFalse( $secondDescription->equals( $firstDescription ) ); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/Language/Description/ValueDescription.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class ValueDescription extends Description implements \Ask\Immutable { 26 | 27 | // This list has values backwards compatible with SMW_CMP_. 28 | const COMP_EQUAL = 1; 29 | const COMP_LEQ = 2; // Less than or equal 30 | const COMP_GEQ = 3; // Greater than or equal 31 | const COMP_NEQ = 4; // Not equal 32 | const COMP_LIKE = 5; 33 | const COMP_NLIKE = 6; // Not like 34 | const COMP_LESS = 7; // Strictly less than 35 | const COMP_GREATER = 8; // Strictly greater than 36 | 37 | /** 38 | * The value to compare to. 39 | * 40 | * @since 1.0 41 | * 42 | * @var DataValue 43 | */ 44 | protected $value; 45 | 46 | /** 47 | * The comparator to use to determine if the value matches. 48 | * 49 | * @since 1.0 50 | * 51 | * @var int 52 | */ 53 | protected $comparator; 54 | 55 | /** 56 | * @since 1.0 57 | * 58 | * @param DataValue $value 59 | * @param int $comparator 60 | * 61 | * @throws InvalidArgumentException 62 | */ 63 | public function __construct( DataValue $value, $comparator = self::COMP_EQUAL ) { 64 | $this->assertComparatorValidity( $comparator ); 65 | 66 | $this->value = $value; 67 | $this->comparator = $comparator; 68 | } 69 | 70 | protected function assertComparatorValidity( $comparator ) { 71 | if ( !is_int( $comparator ) || $comparator < self::COMP_EQUAL || $comparator > self::COMP_GREATER ) { 72 | throw new InvalidArgumentException( 'Invalid comparator specified' ); 73 | } 74 | } 75 | 76 | /** 77 | * Returns the value to compare against. 78 | * 79 | * @since 1.0 80 | * 81 | * @return DataValue 82 | */ 83 | public function getValue() { 84 | return $this->value; 85 | } 86 | 87 | /** 88 | * Returns the comparator to use when comparing against the value. 89 | * 90 | * @since 1.0 91 | * 92 | * @return int 93 | */ 94 | public function getComparator() { 95 | return $this->comparator; 96 | } 97 | 98 | /** 99 | * {@inheritdoc} 100 | * 101 | * @since 1.0 102 | * 103 | * @return integer 104 | */ 105 | public function getSize() { 106 | return 1; 107 | } 108 | 109 | /** 110 | * {@inheritdoc} 111 | * 112 | * @since 1.0 113 | * 114 | * @return integer 115 | */ 116 | public function getDepth() { 117 | return 0; 118 | } 119 | 120 | /** 121 | * {@inheritdoc} 122 | * 123 | * @since 1.0 124 | * 125 | * @return string 126 | */ 127 | public function getType() { 128 | return 'valueDescription'; 129 | } 130 | 131 | /** 132 | * @see Comparable::equals 133 | * 134 | * @since 1.0 135 | * 136 | * @param mixed $mixed 137 | * 138 | * @return boolean 139 | */ 140 | public function equals( $mixed ) { 141 | return $mixed instanceof ValueDescription 142 | && $this->comparator === $mixed->getComparator() 143 | && $this->value->equals( $mixed->getValue() ); 144 | } 145 | 146 | /** 147 | * @see Hashable::getHash 148 | * 149 | * @since 1.0 150 | * 151 | * @return string 152 | */ 153 | public function getHash() { 154 | return sha1( $this->getType() . $this->value->getHash() . $this->comparator ); 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/Language/Description/DescriptionCollection.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | abstract class DescriptionCollection extends Description implements \Ask\Immutable { 16 | 17 | /** 18 | * The descriptions that make up this collection of descriptions. 19 | * 20 | * @var Description[] 21 | */ 22 | private $descriptions; 23 | 24 | /** 25 | * Cache for the hash. 26 | * 27 | * @var string|null 28 | */ 29 | private $hash = null; 30 | 31 | /** 32 | * @since 1.0 33 | * 34 | * @param Description[] $descriptions 35 | */ 36 | public function __construct( array $descriptions ) { 37 | $this->descriptions = $descriptions; 38 | } 39 | 40 | /** 41 | * {@inheritdoc} 42 | * 43 | * @since 1.0 44 | * 45 | * @return integer 46 | */ 47 | public function getSize() { 48 | $size = 0; 49 | 50 | foreach ( $this->descriptions as $description ) { 51 | $size += $description->getSize(); 52 | } 53 | 54 | assert( $size >= 0 ); 55 | 56 | return $size; 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | * 62 | * @since 1.0 63 | * 64 | * @return integer 65 | */ 66 | public function getDepth() { 67 | $depth = 0; 68 | 69 | foreach ( $this->descriptions as $description ) { 70 | $depth = max( $depth, $description->getDepth() ); 71 | } 72 | 73 | assert( $depth >= 0 ); 74 | 75 | return $depth; 76 | } 77 | 78 | /** 79 | * Returns the descriptions that make up this collection of descriptions. 80 | * 81 | * @since 1.0 82 | * 83 | * @return Description[] 84 | */ 85 | public function getDescriptions() { 86 | return $this->descriptions; 87 | } 88 | 89 | /** 90 | * @see Comparable::equals 91 | * 92 | * Note: it is possible this method provides false negatives due to 93 | * equivalent expressions being expressed in different structures. 94 | * This is however likely not important. 95 | * 96 | * @since 1.0 97 | * 98 | * @param mixed $mixed 99 | * 100 | * @return boolean 101 | */ 102 | public function equals( $mixed ) { 103 | if ( !is_object( $mixed ) 104 | || ( get_class( $mixed ) !== get_called_class() ) ) { 105 | return false; 106 | } 107 | 108 | $descriptions = $this->descriptions; 109 | $moreDescriptions = $mixed->getDescriptions(); 110 | 111 | if ( count( $descriptions ) !== count( $moreDescriptions ) ) { 112 | return false; 113 | } 114 | 115 | $this->sortCollection( $descriptions ); 116 | $this->sortCollection( $moreDescriptions ); 117 | reset( $moreDescriptions ); 118 | 119 | foreach ( $descriptions as $description ) { 120 | if ( !$description->equals( current( $moreDescriptions ) ) ) { 121 | return false; 122 | } 123 | 124 | next( $moreDescriptions ); 125 | } 126 | 127 | return true; 128 | } 129 | 130 | /** 131 | * @see Hashable::getHash 132 | * 133 | * @since 1.0 134 | * 135 | * @return string 136 | */ 137 | public function getHash() { 138 | if ( $this->hash === null ) { 139 | $this->sortCollection( $this->descriptions ); 140 | 141 | $this->hash = sha1( 142 | $this->getType() . 143 | implode( 144 | '|', 145 | array_map( 146 | function( Hashable $hashable ) { 147 | return $hashable->getHash(); 148 | }, 149 | $this->descriptions 150 | ) 151 | ) 152 | ); 153 | } 154 | 155 | return $this->hash; 156 | } 157 | 158 | /** 159 | * Does an associative sort that works for Hashable objects. 160 | * 161 | * @since 1.0 162 | * 163 | * @param Hashable[] $array 164 | */ 165 | final protected function sortCollection( array &$array ) { 166 | usort( 167 | $array, 168 | function ( Hashable $a, Hashable $b ) { 169 | return $a->getHash() > $b->getHash() ? 1 : -1; 170 | } 171 | ); 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /src/Language/Description/SomeProperty.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | final class SomeProperty extends Description implements \Ask\Immutable { 24 | 25 | /** 26 | * The property that should be present. 27 | * 28 | * @var DataValue 29 | */ 30 | private $propertyId; 31 | 32 | /** 33 | * The description the properties value should match. 34 | * 35 | * @var Description 36 | */ 37 | private $subDescription; 38 | 39 | /** 40 | * If the property is a sub property or not. 41 | * 42 | * For instance in the Wikibase Claim context, 43 | * a non-sub property would point to the property 44 | * of the main snak, while a sub property would 45 | * point to a qualifier. 46 | * 47 | * @var boolean 48 | */ 49 | private $isSubProperty; 50 | 51 | /** 52 | * Cache for the hash. 53 | * 54 | * @var string|null 55 | */ 56 | private $hash = null; 57 | 58 | /** 59 | * @since 1.0 60 | * 61 | * @param DataValue $propertyId 62 | * @param Description $subDescription 63 | * @param boolean $isSubProperty 64 | * 65 | * @throws InvalidArgumentException 66 | */ 67 | public function __construct( DataValue $propertyId, Description $subDescription, $isSubProperty = false ) { 68 | $this->propertyId = $propertyId; 69 | $this->subDescription = $subDescription; 70 | 71 | if ( !is_bool( $isSubProperty ) ) { 72 | throw new InvalidArgumentException( '$isSubProperty must be of type boolean' ); 73 | } 74 | 75 | $this->isSubProperty = $isSubProperty; 76 | } 77 | 78 | /** 79 | * Returns the description. 80 | * 81 | * @since 1.0 82 | * 83 | * @return Description 84 | */ 85 | public function getSubDescription() { 86 | return $this->subDescription; 87 | } 88 | 89 | /** 90 | * Returns the property. 91 | * 92 | * @since 1.0 93 | * 94 | * @return DataValue 95 | */ 96 | public function getPropertyId() { 97 | return $this->propertyId; 98 | } 99 | 100 | /** 101 | * Returns if the property is a sub property. 102 | * 103 | * @since 1.0 104 | * 105 | * @return boolean 106 | */ 107 | public function isSubProperty() { 108 | return $this->isSubProperty; 109 | } 110 | 111 | /** 112 | * {@inheritdoc} 113 | * 114 | * @since 1.0 115 | * 116 | * @return integer 117 | */ 118 | public function getSize() { 119 | return $this->subDescription->getSize() + 1; 120 | } 121 | 122 | /** 123 | * {@inheritdoc} 124 | * 125 | * @since 1.0 126 | * 127 | * @return integer 128 | */ 129 | public function getDepth() { 130 | return $this->subDescription->getDepth() + 1; 131 | } 132 | 133 | /** 134 | * {@inheritdoc} 135 | * 136 | * @since 1.0 137 | * 138 | * @return string 139 | */ 140 | public function getType() { 141 | return 'someProperty'; 142 | } 143 | 144 | /** 145 | * @see Comparable::equals 146 | * 147 | * @since 1.0 148 | * 149 | * @param mixed $mixed 150 | * 151 | * @return boolean 152 | */ 153 | public function equals( $mixed ) { 154 | return $mixed instanceof SomeProperty 155 | && $this->isSubProperty === $mixed->isSubProperty() 156 | && $this->propertyId->equals( $mixed->getPropertyId() ) 157 | && $this->subDescription->equals( $mixed->getSubDescription() ); 158 | } 159 | 160 | /** 161 | * @see Hashable::getHash 162 | * 163 | * @since 1.0 164 | * 165 | * @return string 166 | */ 167 | public function getHash() { 168 | if ( $this->hash === null ) { 169 | $this->hash = sha1( 170 | $this->getType() . 171 | $this->propertyId->getHash() . 172 | $this->subDescription->getHash() . 173 | $this->isSubProperty 174 | ); 175 | } 176 | 177 | return $this->hash; 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This library is no longer under development and is no longer maintained. 2 | 3 | # Ask 4 | 5 | Library containing a PHP implementation of the Ask query language. 6 | 7 | The implementation consists out of domain objects that represent various parts of Ask queries. 8 | 9 | [![Build Status](https://secure.travis-ci.org/wmde/Ask.png?branch=master)](http://travis-ci.org/wmde/Ask) 10 | [![Coverage Status](https://coveralls.io/repos/wmde/Ask/badge.png?branch=master)](https://coveralls.io/r/wmde/Ask?branch=master) 11 | [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/wmde/Ask/badges/quality-score.png?s=ea4d657f3222ea00305d57bea0339a489882ee95)](https://scrutinizer-ci.com/g/wmde/Ask/) 12 | 13 | On Packagist: 14 | [![Latest Stable Version](https://poser.pugx.org/ask/ask/version.png)](https://packagist.org/packages/ask/ask) 15 | [![Download count](https://poser.pugx.org/ask/ask/d/total.png)](https://packagist.org/packages/ask/ask) 16 | 17 | ## Requirements 18 | 19 | * PHP 5.3 or later 20 | * [DataValues](https://github.com/DataValues/DataValues) 0.1 or later 21 | 22 | ## Installation 23 | 24 | You can use [Composer](http://getcomposer.org/) to download and install 25 | this package as well as its dependencies. Alternatively you can simply clone 26 | the git repository and take care of loading yourself. 27 | 28 | ### Composer 29 | 30 | To add this package as a local, per-project dependency to your project, simply add a 31 | dependency on `ask/ask` to your project's `composer.json` file. 32 | Here is a minimal example of a `composer.json` file that just defines a dependency on 33 | Ask 1.0: 34 | 35 | { 36 | "require": { 37 | "ask/ask": "1.0.*" 38 | } 39 | } 40 | 41 | ### Manual 42 | 43 | Get the Ask code, either via git, or some other means. Also get all dependencies. 44 | You can find a list of the dependencies in the "require" section of the composer.json file. 45 | Load all dependencies and the load the Ask library by including its entry point: 46 | Ask.php. 47 | 48 | ## Structure 49 | 50 | The Ask library defines the Ask query language. Its important components are: 51 | 52 | * Ask\Language - everything part of the ask language itself 53 | 54 | * Ask\Language\Description - descriptions (aka concepts) 55 | * Ask\Language\Option - QueryOptions object and its parts 56 | * Ask\Language\Selection - selection requests 57 | * Ask\Language\Query.php - the object defining what a query is 58 | 59 | ### Description 60 | 61 | Each query has a single description which specifies which entities match. This is similar to the 62 | WHERE part of an SQL string. There different types of descriptions are listed below. Since several 63 | types of descriptions can be composed out of one or more sub descriptions, tree like structures can 64 | be created. 65 | 66 | * Description - abstract base class 67 | 68 | * AnyValue - A description that matches any object 69 | * Conjunction - Description of a collection of many descriptions, all of which must be satisfied (AND) 70 | * Disjunction - Description of a collection of many descriptions, at least one of which must be satisfied (OR) 71 | * SomeProperty - Description of a set of instances that have an attribute with some value that fits another (sub)description 72 | * ValueDescription - Description of one data value, or of a range of data values 73 | 74 | All descriptions reside in the Ask\Language\Description namespace. 75 | 76 | ### Option 77 | 78 | The options a query consist out of are defined by the QueryOptions class. This class 79 | contains limit, offset and sorting options. 80 | 81 | Sorting options are defined by the SortOptions class, which contains a list of 82 | SortExpression objects. 83 | 84 | All options related classes reside in the Ask\Language\Option namespace. 85 | 86 | ### Selection 87 | 88 | Specifying what information a query should select from matching entities is done via the selection 89 | requests in the query object. Selection requests are thus akin to the SELECT part of an SQL string. 90 | They thus have no effect on which entities match the query and are returned. All types of selection 91 | request implement abstract base class SelectionRequest and can be found in the Ask\Language\Selection 92 | namespace. 93 | 94 | ## Usage 95 | 96 | #### A query for the first hundred entities that are compared 97 | 98 | ```php 99 | use Ask\Language\Query; 100 | use Ask\Language\Description\AnyValue; 101 | use Ask\Language\Option\QueryOptions; 102 | 103 | $myAwesomeQuery = new Query( 104 | new AnyValue(), 105 | array(), 106 | new QueryOptions( 100, 0 ) 107 | ); 108 | ``` 109 | 110 | #### A query with an offset of 50 111 | 112 | ```php 113 | $myAwesomeQuery = new Query( 114 | new AnyValue(), 115 | array(), 116 | new QueryOptions( 100, 50 ) 117 | ); 118 | ``` 119 | 120 | #### A query to get the ''cost'' of the first hundred entities that have a ''cost'' property 121 | 122 | This is assuming 'p42' is an identifier for a ''cost'' property. 123 | 124 | ```php 125 | $awesomePropertyId = new PropertyValue( 'p42' ); 126 | 127 | $myAwesomeQuery = new Query( 128 | new SomeProperty( $awesomePropertyId, new AnyValue() ), 129 | array( 130 | new PropertySelection( $awesomePropertyId ) 131 | ), 132 | new QueryOptions( 100, 0 ) 133 | ); 134 | ``` 135 | 136 | #### A query to get the first hundred entities that have 9000.1 as value for their ''cost'' property. 137 | 138 | This is assuming 'p42' is an identifier for a ''cost'' property. 139 | 140 | ```php 141 | $awesomePropertyId = new PropertyValue( 'p42' ); 142 | $someCost = new NumericValue( 9000.1 ); 143 | 144 | $myAwesomeQuery = new Query( 145 | new SomeProperty( $awesomePropertyId, new ValueDescription( $someCost ) ), 146 | array(), 147 | new QueryOptions( 100, 0 ) 148 | ); 149 | ``` 150 | 151 | #### A query getting the hundred entities with highest ''cost'', highest ''cost'' first 152 | 153 | This is assuming 'p42' is an identifier for a ''cost'' property. 154 | 155 | ```php 156 | $awesomePropertyId = new PropertyValue( 'p42' ); 157 | 158 | $myAwesomeQuery = new Query( 159 | new AnyValue(), 160 | array(), 161 | new QueryOptions( 162 | 100, 163 | 0, 164 | new SortOptions( array( 165 | new PropertyValueSortExpression( $awesomePropertyId, SortExpression::DESCENDING ) 166 | ) ) 167 | ) 168 | ); 169 | ``` 170 | 171 | #### A query to get the hundred first entities that have a ''cost'' either equal to 42 or bigger than 9000 172 | 173 | This is assuming 'p42' is an identifier for a ''cost'' property. 174 | 175 | ```php 176 | $awesomePropertyId = new PropertyValue( 'p42' ); 177 | $costOf42 = new NumericValue( 42 ); 178 | $costOf9000 = new NumericValue( 9000 ); 179 | 180 | $myAwesomeQuery = new Query( 181 | new SomeProperty( 182 | $awesomePropertyId, 183 | new Disjunction( array( 184 | new ValueDescription( $costOf42 ), 185 | new ValueDescription( $costOf9000, ValueDescription::COMP_GRTR ), 186 | ) ) 187 | ), 188 | array(), 189 | new QueryOptions( 100, 0 ) 190 | ); 191 | ``` 192 | 193 | ## Tests 194 | 195 | This library comes with a set up PHPUnit tests that cover all non-trivial code. You can run these 196 | tests using the PHPUnit configuration file found in the root directory. The tests can also be run 197 | via TravisCI, as a TravisCI configuration file is also provided in the root directory. 198 | 199 | ## Authors 200 | 201 | Ask has been written by [Jeroen De Dauw](https://www.mediawiki.org/wiki/User:Jeroen_De_Dauw) 202 | as [Wikimedia Germany](https://wikimedia.de) employee for the [Wikidata project](https://wikidata.org/). 203 | 204 | This library is a reimplementation of the Ask language domain objects in 205 | [Semantic MediaWiki](https://semantic-mediawiki.org/), which have been written by 206 | [Markus Krötzsch](http://korrekt.org/). This reimplementation is conceptually almost 207 | entirely based on the original code and contains small portions of it. 208 | 209 | ## Release notes 210 | 211 | ### 1.0.3 (alpha) 212 | 213 | * Installation together with DataValues 2.x is now supported. 214 | 215 | ### 1.0.2 (2014-10-16) 216 | 217 | * Installation together with DataValues 1.x is now supported. 218 | 219 | ### 1.0.1 (2014-01-21) 220 | 221 | * Removed custom autoloader. The Composer support for PSR-4 is now used. 222 | * The PHPUnit bootstrap file now automatically runs "composer update". 223 | 224 | ### 1.0 (2013-11-17) 225 | 226 | Initial release with these features: 227 | 228 | * PHP implementation of the Ask language core 229 | * Implementation of descriptions, selection requests and sort options initially needed for Wikidata 230 | 231 | ## Links 232 | 233 | * [Ask on Packagist](https://packagist.org/packages/ask/ask) 234 | * [Ask on Ohloh](https://www.ohloh.net/p/ask) 235 | * [Ask on coveralls.io](https://coveralls.io/r/wmde/Ask?branch=master) 236 | * [Ask on ScrutinizerCI](https://scrutinizer-ci.com/g/wmde/Ask/) 237 | * [Ask on TravisCI](https://travis-ci.org/wmde/Ask) 238 | * [NodeJS implementation of Ask](https://github.com/JeroenDeDauw/AskJS) 239 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | The license text below "----" applies to all files within this distribution, other 2 | than those that are in a directory which contains files named "LICENSE" or 3 | "COPYING", or a subdirectory thereof. For those files, the license text contained in 4 | said file overrides any license information contained in directories of smaller depth. 5 | Alternative licenses are typically used for software that is provided by external 6 | parties, and merely packaged with this software for convenience. 7 | ---- 8 | 9 | GNU GENERAL PUBLIC LICENSE 10 | Version 2, June 1991 11 | 12 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 13 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 14 | Everyone is permitted to copy and distribute verbatim copies 15 | of this license document, but changing it is not allowed. 16 | 17 | Preamble 18 | 19 | The licenses for most software are designed to take away your 20 | freedom to share and change it. By contrast, the GNU General Public 21 | License is intended to guarantee your freedom to share and change free 22 | software--to make sure the software is free for all its users. This 23 | General Public License applies to most of the Free Software 24 | Foundation's software and to any other program whose authors commit to 25 | using it. (Some other Free Software Foundation software is covered by 26 | the GNU Lesser General Public License instead.) You can apply it to 27 | your programs, too. 28 | 29 | When we speak of free software, we are referring to freedom, not 30 | price. Our General Public Licenses are designed to make sure that you 31 | have the freedom to distribute copies of free software (and charge for 32 | this service if you wish), that you receive source code or can get it 33 | if you want it, that you can change the software or use pieces of it 34 | in new free programs; and that you know you can do these things. 35 | 36 | To protect your rights, we need to make restrictions that forbid 37 | anyone to deny you these rights or to ask you to surrender the rights. 38 | These restrictions translate to certain responsibilities for you if you 39 | distribute copies of the software, or if you modify it. 40 | 41 | For example, if you distribute copies of such a program, whether 42 | gratis or for a fee, you must give the recipients all the rights that 43 | you have. You must make sure that they, too, receive or can get the 44 | source code. And you must show them these terms so they know their 45 | rights. 46 | 47 | We protect your rights with two steps: (1) copyright the software, and 48 | (2) offer you this license which gives you legal permission to copy, 49 | distribute and/or modify the software. 50 | 51 | Also, for each author's protection and ours, we want to make certain 52 | that everyone understands that there is no warranty for this free 53 | software. If the software is modified by someone else and passed on, we 54 | want its recipients to know that what they have is not the original, so 55 | that any problems introduced by others will not reflect on the original 56 | authors' reputations. 57 | 58 | Finally, any free program is threatened constantly by software 59 | patents. We wish to avoid the danger that redistributors of a free 60 | program will individually obtain patent licenses, in effect making the 61 | program proprietary. To prevent this, we have made it clear that any 62 | patent must be licensed for everyone's free use or not licensed at all. 63 | 64 | The precise terms and conditions for copying, distribution and 65 | modification follow. 66 | 67 | GNU GENERAL PUBLIC LICENSE 68 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 69 | 70 | 0. This License applies to any program or other work which contains 71 | a notice placed by the copyright holder saying it may be distributed 72 | under the terms of this General Public License. The "Program", below, 73 | refers to any such program or work, and a "work based on the Program" 74 | means either the Program or any derivative work under copyright law: 75 | that is to say, a work containing the Program or a portion of it, 76 | either verbatim or with modifications and/or translated into another 77 | language. (Hereinafter, translation is included without limitation in 78 | the term "modification".) Each licensee is addressed as "you". 79 | 80 | Activities other than copying, distribution and modification are not 81 | covered by this License; they are outside its scope. The act of 82 | running the Program is not restricted, and the output from the Program 83 | is covered only if its contents constitute a work based on the 84 | Program (independent of having been made by running the Program). 85 | Whether that is true depends on what the Program does. 86 | 87 | 1. You may copy and distribute verbatim copies of the Program's 88 | source code as you receive it, in any medium, provided that you 89 | conspicuously and appropriately publish on each copy an appropriate 90 | copyright notice and disclaimer of warranty; keep intact all the 91 | notices that refer to this License and to the absence of any warranty; 92 | and give any other recipients of the Program a copy of this License 93 | along with the Program. 94 | 95 | You may charge a fee for the physical act of transferring a copy, and 96 | you may at your option offer warranty protection in exchange for a fee. 97 | 98 | 2. You may modify your copy or copies of the Program or any portion 99 | of it, thus forming a work based on the Program, and copy and 100 | distribute such modifications or work under the terms of Section 1 101 | above, provided that you also meet all of these conditions: 102 | 103 | a) You must cause the modified files to carry prominent notices 104 | stating that you changed the files and the date of any change. 105 | 106 | b) You must cause any work that you distribute or publish, that in 107 | whole or in part contains or is derived from the Program or any 108 | part thereof, to be licensed as a whole at no charge to all third 109 | parties under the terms of this License. 110 | 111 | c) If the modified program normally reads commands interactively 112 | when run, you must cause it, when started running for such 113 | interactive use in the most ordinary way, to print or display an 114 | announcement including an appropriate copyright notice and a 115 | notice that there is no warranty (or else, saying that you provide 116 | a warranty) and that users may redistribute the program under 117 | these conditions, and telling the user how to view a copy of this 118 | License. (Exception: if the Program itself is interactive but 119 | does not normally print such an announcement, your work based on 120 | the Program is not required to print an announcement.) 121 | 122 | These requirements apply to the modified work as a whole. If 123 | identifiable sections of that work are not derived from the Program, 124 | and can be reasonably considered independent and separate works in 125 | themselves, then this License, and its terms, do not apply to those 126 | sections when you distribute them as separate works. But when you 127 | distribute the same sections as part of a whole which is a work based 128 | on the Program, the distribution of the whole must be on the terms of 129 | this License, whose permissions for other licensees extend to the 130 | entire whole, and thus to each and every part regardless of who wrote it. 131 | 132 | Thus, it is not the intent of this section to claim rights or contest 133 | your rights to work written entirely by you; rather, the intent is to 134 | exercise the right to control the distribution of derivative or 135 | collective works based on the Program. 136 | 137 | In addition, mere aggregation of another work not based on the Program 138 | with the Program (or with a work based on the Program) on a volume of 139 | a storage or distribution medium does not bring the other work under 140 | the scope of this License. 141 | 142 | 3. You may copy and distribute the Program (or a work based on it, 143 | under Section 2) in object code or executable form under the terms of 144 | Sections 1 and 2 above provided that you also do one of the following: 145 | 146 | a) Accompany it with the complete corresponding machine-readable 147 | source code, which must be distributed under the terms of Sections 148 | 1 and 2 above on a medium customarily used for software interchange; or, 149 | 150 | b) Accompany it with a written offer, valid for at least three 151 | years, to give any third party, for a charge no more than your 152 | cost of physically performing source distribution, a complete 153 | machine-readable copy of the corresponding source code, to be 154 | distributed under the terms of Sections 1 and 2 above on a medium 155 | customarily used for software interchange; or, 156 | 157 | c) Accompany it with the information you received as to the offer 158 | to distribute corresponding source code. (This alternative is 159 | allowed only for noncommercial distribution and only if you 160 | received the program in object code or executable form with such 161 | an offer, in accord with Subsection b above.) 162 | 163 | The source code for a work means the preferred form of the work for 164 | making modifications to it. For an executable work, complete source 165 | code means all the source code for all modules it contains, plus any 166 | associated interface definition files, plus the scripts used to 167 | control compilation and installation of the executable. However, as a 168 | special exception, the source code distributed need not include 169 | anything that is normally distributed (in either source or binary 170 | form) with the major components (compiler, kernel, and so on) of the 171 | operating system on which the executable runs, unless that component 172 | itself accompanies the executable. 173 | 174 | If distribution of executable or object code is made by offering 175 | access to copy from a designated place, then offering equivalent 176 | access to copy the source code from the same place counts as 177 | distribution of the source code, even though third parties are not 178 | compelled to copy the source along with the object code. 179 | 180 | 4. You may not copy, modify, sublicense, or distribute the Program 181 | except as expressly provided under this License. Any attempt 182 | otherwise to copy, modify, sublicense or distribute the Program is 183 | void, and will automatically terminate your rights under this License. 184 | However, parties who have received copies, or rights, from you under 185 | this License will not have their licenses terminated so long as such 186 | parties remain in full compliance. 187 | 188 | 5. You are not required to accept this License, since you have not 189 | signed it. However, nothing else grants you permission to modify or 190 | distribute the Program or its derivative works. These actions are 191 | prohibited by law if you do not accept this License. Therefore, by 192 | modifying or distributing the Program (or any work based on the 193 | Program), you indicate your acceptance of this License to do so, and 194 | all its terms and conditions for copying, distributing or modifying 195 | the Program or works based on it. 196 | 197 | 6. Each time you redistribute the Program (or any work based on the 198 | Program), the recipient automatically receives a license from the 199 | original licensor to copy, distribute or modify the Program subject to 200 | these terms and conditions. You may not impose any further 201 | restrictions on the recipients' exercise of the rights granted herein. 202 | You are not responsible for enforcing compliance by third parties to 203 | this License. 204 | 205 | 7. If, as a consequence of a court judgment or allegation of patent 206 | infringement or for any other reason (not limited to patent issues), 207 | conditions are imposed on you (whether by court order, agreement or 208 | otherwise) that contradict the conditions of this License, they do not 209 | excuse you from the conditions of this License. If you cannot 210 | distribute so as to satisfy simultaneously your obligations under this 211 | License and any other pertinent obligations, then as a consequence you 212 | may not distribute the Program at all. For example, if a patent 213 | license would not permit royalty-free redistribution of the Program by 214 | all those who receive copies directly or indirectly through you, then 215 | the only way you could satisfy both it and this License would be to 216 | refrain entirely from distribution of the Program. 217 | 218 | If any portion of this section is held invalid or unenforceable under 219 | any particular circumstance, the balance of the section is intended to 220 | apply and the section as a whole is intended to apply in other 221 | circumstances. 222 | 223 | It is not the purpose of this section to induce you to infringe any 224 | patents or other property right claims or to contest validity of any 225 | such claims; this section has the sole purpose of protecting the 226 | integrity of the free software distribution system, which is 227 | implemented by public license practices. Many people have made 228 | generous contributions to the wide range of software distributed 229 | through that system in reliance on consistent application of that 230 | system; it is up to the author/donor to decide if he or she is willing 231 | to distribute software through any other system and a licensee cannot 232 | impose that choice. 233 | 234 | This section is intended to make thoroughly clear what is believed to 235 | be a consequence of the rest of this License. 236 | 237 | 8. If the distribution and/or use of the Program is restricted in 238 | certain countries either by patents or by copyrighted interfaces, the 239 | original copyright holder who places the Program under this License 240 | may add an explicit geographical distribution limitation excluding 241 | those countries, so that distribution is permitted only in or among 242 | countries not thus excluded. In such case, this License incorporates 243 | the limitation as if written in the body of this License. 244 | 245 | 9. The Free Software Foundation may publish revised and/or new versions 246 | of the General Public License from time to time. Such new versions will 247 | be similar in spirit to the present version, but may differ in detail to 248 | address new problems or concerns. 249 | 250 | Each version is given a distinguishing version number. If the Program 251 | specifies a version number of this License which applies to it and "any 252 | later version", you have the option of following the terms and conditions 253 | either of that version or of any later version published by the Free 254 | Software Foundation. If the Program does not specify a version number of 255 | this License, you may choose any version ever published by the Free Software 256 | Foundation. 257 | 258 | 10. If you wish to incorporate parts of the Program into other free 259 | programs whose distribution conditions are different, write to the author 260 | to ask for permission. For software which is copyrighted by the Free 261 | Software Foundation, write to the Free Software Foundation; we sometimes 262 | make exceptions for this. Our decision will be guided by the two goals 263 | of preserving the free status of all derivatives of our free software and 264 | of promoting the sharing and reuse of software generally. 265 | 266 | NO WARRANTY 267 | 268 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 269 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 270 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 271 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 272 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 273 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 274 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 275 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 276 | REPAIR OR CORRECTION. 277 | 278 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 279 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 280 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 281 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 282 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 283 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 284 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 285 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 286 | POSSIBILITY OF SUCH DAMAGES. 287 | 288 | END OF TERMS AND CONDITIONS 289 | 290 | How to Apply These Terms to Your New Programs 291 | 292 | If you develop a new program, and you want it to be of the greatest 293 | possible use to the public, the best way to achieve this is to make it 294 | free software which everyone can redistribute and change under these terms. 295 | 296 | To do so, attach the following notices to the program. It is safest 297 | to attach them to the start of each source file to most effectively 298 | convey the exclusion of warranty; and each file should have at least 299 | the "copyright" line and a pointer to where the full notice is found. 300 | 301 | 302 | Copyright (C) 303 | 304 | This program is free software; you can redistribute it and/or modify 305 | it under the terms of the GNU General Public License as published by 306 | the Free Software Foundation; either version 2 of the License, or 307 | (at your option) any later version. 308 | 309 | This program is distributed in the hope that it will be useful, 310 | but WITHOUT ANY WARRANTY; without even the implied warranty of 311 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 312 | GNU General Public License for more details. 313 | 314 | You should have received a copy of the GNU General Public License along 315 | with this program; if not, write to the Free Software Foundation, Inc., 316 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 317 | 318 | Also add information on how to contact you by electronic and paper mail. 319 | 320 | If the program is interactive, make it output a short notice like this 321 | when it starts in an interactive mode: 322 | 323 | Gnomovision version 69, Copyright (C) year name of author 324 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 325 | This is free software, and you are welcome to redistribute it 326 | under certain conditions; type `show c' for details. 327 | 328 | The hypothetical commands `show w' and `show c' should show the appropriate 329 | parts of the General Public License. Of course, the commands you use may 330 | be called something other than `show w' and `show c'; they could even be 331 | mouse-clicks or menu items--whatever suits your program. 332 | 333 | You should also get your employer (if you work as a programmer) or your 334 | school, if any, to sign a "copyright disclaimer" for the program, if 335 | necessary. Here is a sample; alter the names: 336 | 337 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 338 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 339 | 340 | , 1 April 1989 341 | Ty Coon, President of Vice 342 | 343 | This General Public License does not permit incorporating your program into 344 | proprietary programs. If your program is a subroutine library, you may 345 | consider it more useful to permit linking proprietary applications with the 346 | library. If this is what you want to do, use the GNU Lesser General 347 | Public License instead of this License. 348 | --------------------------------------------------------------------------------