From 20ea229a8ec146a7525912e970f49ee86bf3ddba Mon Sep 17 00:00:00 2001 From: alistair3149 Date: Fri, 5 Jun 2026 17:42:55 -0400 Subject: [PATCH 1/6] Support Semantic MediaWiki 7.0 Raise the minimum requirements to MediaWiki 1.43 and Semantic MediaWiki 7.0 (PHP 8.1+) and adapt to the SMW 7.0 API changes: - Replace the removed DataValueFactory::newDataItemValue() / newPropertyObjectValue() with newDataValueByItem() / newDataValueByProperty(). - Replace the removed DIProperty::findPropertyTypeId() / setPropertyTypeId() with findPropertyValueType() / setPropertyValueType(). - Rebuild the HTTP response cache without the removed CacheFactory::newMediaWikiCompositeCache(). - Rename the QueryResult::getLink() override to getQueryLink() and match the native return types the SMW 7.0 ResultArray and QueryResult base classes now declare. - Move the deprecated SMW class aliases to their canonical namespaced names (SMW\DataItems\*, SMW\DataValues\DataValue, SMW\Query\Query, SMW\Formatters\Infolink). Also modernise CI (a per-row MariaDB LTS matrix on MediaWiki 1.43-1.45, current action majors, informational coverage) and remove the dead Travis and Scrutinizer configuration. --- .github/workflows/ci.yaml | 36 ++++------ .scrutinizer.yml | 28 -------- .travis.yml | 39 ----------- Makefile | 4 +- RELEASE-NOTES.md | 5 ++ SemanticExternalQueryLookup.php | 2 +- codecov.yml | 10 +++ composer.json | 7 +- extension.json | 6 +- phpmd.xml | 29 -------- src/ByHttpRequest/CannedResultArray.php | 40 +++++------ src/ByHttpRequest/JsonResponseParser.php | 18 ++--- src/ByHttpRequest/QueryResult.php | 16 +++-- src/ByHttpRequest/QueryResultFetcher.php | 2 +- src/ByHttpRequest/ResponsePropertyList.php | 14 ++-- src/ByHttpRequestQueryLookup.php | 20 +++++- src/DataValueDeserializer.php | 46 ++++++------ src/QueryEncoder.php | 4 +- src/QueryResultFactory.php | 2 +- .../JsonParseOnFauxHttpResponseTest.php | 8 +-- .../ByHttpRequest/CannedResultArrayTest.php | 50 ++++++------- .../ByHttpRequest/JsonResponseParserTest.php | 14 ++-- .../ByHttpRequest/QueryResultFetcherTest.php | 14 ++-- .../Unit/ByHttpRequest/QueryResultTest.php | 18 ++--- .../ResponsePropertyListTest.php | 8 +-- .../Unit/ByHttpRequestQueryLookupTest.php | 8 +-- .../Unit/DataValueDeserializerTest.php | 30 ++++---- tests/phpunit/Unit/QueryEncoderTest.php | 2 +- tests/phpunit/Unit/QueryResultFactoryTest.php | 4 +- tests/travis/install-mediawiki.sh | 43 ------------ .../install-semantic-external-query-lookup.sh | 70 ------------------- tests/travis/run-tests.sh | 14 ---- tests/travis/upload-coverage-report.sh | 10 --- 33 files changed, 208 insertions(+), 413 deletions(-) delete mode 100644 .scrutinizer.yml delete mode 100644 .travis.yml delete mode 100644 phpmd.xml delete mode 100644 tests/travis/install-mediawiki.sh delete mode 100644 tests/travis/install-semantic-external-query-lookup.sh delete mode 100644 tests/travis/run-tests.sh delete mode 100644 tests/travis/upload-coverage-report.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c279b05..62be863 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -17,39 +17,32 @@ jobs: strategy: matrix: include: - - mediawiki_version: '1.39' + - mediawiki_version: '1.43' smw_version: dev-master php_version: 8.1 database_type: mysql - database_image: "mariadb:10" + database_image: "mariadb:10.11" coverage: false experimental: false - - mediawiki_version: '1.40' + - mediawiki_version: '1.43' smw_version: dev-master - php_version: 8.1 + php_version: 8.2 database_type: mysql - database_image: "mariadb:11.2" + database_image: "mariadb:11.4" coverage: true experimental: false - - mediawiki_version: '1.41' - smw_version: dev-master - php_version: 8.1 - database_type: mysql - database_image: "mariadb:11.2" - coverage: false - experimental: false - - mediawiki_version: '1.42' + - mediawiki_version: '1.44' smw_version: dev-master - php_version: 8.1 + php_version: 8.3 database_type: mysql - database_image: "mariadb:11.2" + database_image: "mariadb:11.8" coverage: false experimental: false - - mediawiki_version: '1.43' + - mediawiki_version: '1.45' smw_version: dev-master - php_version: 8.1 + php_version: 8.4 database_type: mysql - database_image: "mariadb:11.2" + database_image: "mariadb:12.3" coverage: false experimental: false @@ -60,13 +53,12 @@ jobs: DB_TYPE: ${{ matrix.database_type }} DB_IMAGE: ${{ matrix.database_image }} - steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive - + - name: Update submodules run: git submodule update --init --remote @@ -79,7 +71,7 @@ jobs: if: matrix.coverage == true - name: Upload code coverage - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v6 with: token: ${{ secrets.CODECOV_TOKEN }} files: coverage/php/coverage.xml diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index 6f59b5c..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,28 +0,0 @@ -filter: - excluded_paths: - - 'vendor/*' - -tools: - php_mess_detector: - config: - controversial_rules: { superglobals: false } - php_cpd: true - php_pdepend: true - php_code_coverage: false - php_code_sniffer: true - php_cs_fixer: true - php_loc: true - php_analyzer: true - sensiolabs_security_checker: true - external_code_coverage: - timeout: '900' - -checks: - php: - psr2_class_declaration: false - psr2_switch_declaration: false - sql_injection_vulnerabilities: true - security_vulnerabilities: true - no_eval: true - code_rating: true - duplication: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9d11b33..0000000 --- a/.travis.yml +++ /dev/null @@ -1,39 +0,0 @@ -language: php - -env: - - THENEEDFORTHIS=FAIL - -matrix: - fast_finish: true - include: - - env: DB=mysql; MW=1.25.6; TYPE=coverage - php: 5.6 - - env: DB=mysql; MW=1.23.9; - php: 5.4 - - env: DB=sqlite; MW=1.26.3; SITELANG=ja - php: 5.5 - - env: DB=sqlite; MW=master; PHPUNIT=4.8.* - php: '7' - exclude: - - env: THENEEDFORTHIS=FAIL - allow_failures: - - env: DB=sqlite; MW=master; PHPUNIT=4.7.* - -install: - - bash ./tests/travis/install-mediawiki.sh - - bash ./tests/travis/install-semantic-external-query-lookup.sh - -script: - - bash ./tests/travis/run-tests.sh - -after_success: - - bash ./tests/travis/upload-coverage-report.sh - -notifications: - email: - on_success: change - on_failure: always - -cache: - directories: - - $HOME/.composer/cache diff --git a/Makefile b/Makefile index 0780458..e07c22c 100644 --- a/Makefile +++ b/Makefile @@ -11,10 +11,10 @@ endif EXTENSION=SemanticExternalQueryLookup # docker images -MW_VERSION?=1.39 +MW_VERSION?=1.43 PHP_VERSION?=8.1 DB_TYPE?=mysql -DB_IMAGE?="mariadb:10" +DB_IMAGE?="mariadb:11.4" # extensions SMW_VERSION?=dev-master diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index debf82f..c9a5725 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,10 @@ This file contains the RELEASE-NOTES of the Semantic External Query Lookup (a.k.a. SEQL) extension. +### 2.0.0 (2026-??-??) + +- Added support for Semantic MediaWiki 7.0 +- Raised the minimum requirements to MediaWiki 1.43 and Semantic MediaWiki 7.0 + ### 1.0.0 (2016-??-??) - Initial release diff --git a/SemanticExternalQueryLookup.php b/SemanticExternalQueryLookup.php index eb9afb5..323263f 100644 --- a/SemanticExternalQueryLookup.php +++ b/SemanticExternalQueryLookup.php @@ -17,7 +17,7 @@ class SemanticExternalQueryLookup { * @since 1.0 */ public static function onExtensionFunction() { - define( 'SEQL_VERSION', '1.0.0-alpha' ); + define( 'SEQL_VERSION', '2.0.0-alpha' ); class_alias( 'SEQL\ByHttpRequestQueryLookup', 'SMWExternalQueryLookup' ); // deprecated class_alias( 'SEQL\ByHttpRequestQueryLookup', 'SMWExternalAskQueryLookup' ); diff --git a/codecov.yml b/codecov.yml index 986ab9a..bd9ba87 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,2 +1,12 @@ +coverage: + status: + project: + default: + informational: true + removed_code_behavior: adjust_base + patch: + default: + informational: true + fixes: - "/var/www/html/extensions/SemanticExternalQueryLookup/::" diff --git a/composer.json b/composer.json index 491a15a..3011eff 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.x-dev" } }, "config": { @@ -67,10 +67,11 @@ "phpunit": "php ${MW_INSTALL_PATH:-../..}/tests/phpunit/phpunit.php -c phpunit.xml.dist", "phpunit-coverage": "php ${MW_INSTALL_PATH:-../..}/tests/phpunit/phpunit.php -c phpunit.xml.dist --testdox --coverage-text --coverage-html coverage/php --coverage-clover coverage/php/coverage.xml", "post-test-coverage": [ - "sed -i 's|/var/www/html/extensions/SemanticResultFormats/||g' coverage/php/coverage.xml", + "sed -i 's|/var/www/html/extensions/SemanticExternalQueryLookup/||g' coverage/php/coverage.xml", "find coverage/php -type f -name '*.html' -exec sed -i 's|/var/www/html/extensions/||g' {} +" ], - "integration": "composer phpunit -- --testsuite=semantic-result-formats-integration", + "unit": "composer phpunit -- --testsuite=semantic-external-query-lookup-unit", + "integration": "composer phpunit -- --testsuite=semantic-external-query-lookup-integration", "phpcs": "phpcs -ps -d memory_limit=2G", "phpcs-fix": "phpcbf -p", "lint": "parallel-lint . --exclude vendor --exclude node_modules --exclude extensions", diff --git a/extension.json b/extension.json index 0bc368d..cf88cc8 100644 --- a/extension.json +++ b/extension.json @@ -1,6 +1,6 @@ { "name": "SemanticExternalQueryLookup", - "version": "1.1.0-alpha", + "version": "2.0.0-alpha", "author": [ "James Hong Kong" ], @@ -9,9 +9,9 @@ "license-name": "GPL-2.0-or-later", "type": "semantic", "requires": { - "MediaWiki": ">= 1.39", + "MediaWiki": ">= 1.43", "extensions": { - "SemanticMediaWiki": ">= 4.2" + "SemanticMediaWiki": ">= 7.0" } }, "MessagesDirs": { diff --git a/phpmd.xml b/phpmd.xml deleted file mode 100644 index 1bc93f2..0000000 --- a/phpmd.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ByHttpRequest/CannedResultArray.php b/src/ByHttpRequest/CannedResultArray.php index 5a6907d..474b543 100644 --- a/src/ByHttpRequest/CannedResultArray.php +++ b/src/ByHttpRequest/CannedResultArray.php @@ -2,13 +2,13 @@ namespace SEQL\ByHttpRequest; +use SMW\DataItems\DataItem; +use SMW\DataItems\Property; +use SMW\DataItems\WikiPage; use SMW\DataValueFactory; -use SMW\DIProperty; -use SMW\DIWikiPage; +use SMW\DataValues\DataValue; use SMW\Query\PrintRequest; use SMW\Query\Result\ResultArray; -use SMWDataItem; -use SMWDataValue as DataValue; /** * @license GPL-2.0-or-later @@ -24,7 +24,7 @@ class CannedResultArray extends ResultArray { private $mPrintRequest; /** - * @var DIWikiPage + * @var WikiPage */ private $mResult; @@ -41,11 +41,11 @@ class CannedResultArray extends ResultArray { /** * @since 1.0 * - * @param DIWikiPage $resultPage + * @param WikiPage $resultPage * @param PrintRequest $printRequest * @param JsonResponseParser $jsonResponseParser */ - public function __construct( DIWikiPage $resultPage, PrintRequest $printRequest, JsonResponseParser $jsonResponseParser ) { + public function __construct( WikiPage $resultPage, PrintRequest $printRequest, JsonResponseParser $jsonResponseParser ) { $this->mResult = $resultPage; $this->mPrintRequest = $printRequest; $this->jsonResponseParser = $jsonResponseParser; @@ -55,18 +55,18 @@ public function __construct( DIWikiPage $resultPage, PrintRequest $printRequest, /** * @see ResultArray::getResultSubject * - * @return DIWikiPage + * @return WikiPage */ - public function getResultSubject() { + public function getResultSubject(): WikiPage { return $this->mResult; } /** * @see ResultArray::getContent * - * @return SMWDataItem[]|false + * @return DataItem[]|false */ - public function getContent() { + public function getContent(): array|false { $this->loadContent(); if ( !$this->mContent ) { @@ -87,7 +87,7 @@ public function getContent() { * * @return PrintRequest */ - public function getPrintRequest() { + public function getPrintRequest(): PrintRequest { return $this->mPrintRequest; } @@ -96,7 +96,7 @@ public function getPrintRequest() { * * @since 1.6 * - * @return SMWDataItem|false + * @return DataItem|false */ public function getNextDataItem() { $this->loadContent(); @@ -111,9 +111,9 @@ public function getNextDataItem() { * * @since 1.7.1 * - * @return SMWDataItem|false + * @return mixed */ - public function reset() { + public function reset(): mixed { $this->loadContent(); $result = reset( $this->mContent ); @@ -142,12 +142,12 @@ public function getNextDataValue() { // therefore don't try to recreate a DataValue and use the DV created // from the raw API response if ( $this->mPrintRequest->getMode() === PrintRequest::PRINT_PROP && - $property->findPropertyTypeId() === '_qty' ) { + $property->findPropertyValueType() === '_qty' ) { return $content; } if ( $this->mPrintRequest->getMode() === PrintRequest::PRINT_PROP && - strpos( $property->findPropertyTypeId(), '_rec' ) !== false ) { + strpos( $property->findPropertyValueType(), '_rec' ) !== false ) { if ( $this->mPrintRequest->getParameter( 'index' ) === false ) { return $content; @@ -180,7 +180,7 @@ public function getNextDataValue() { $content = $content->getDataItem(); } - $dataValue = DataValueFactory::getInstance()->newDataItemValue( + $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $content, $diProperty ); @@ -215,7 +215,7 @@ protected function loadContent() { case PrintRequest::PRINT_CATS: $this->mContent = $this->jsonResponseParser->getPropertyValuesFor( $this->mResult, - new DIProperty( '_INST' ) + new Property( '_INST' ) ); break; @@ -251,7 +251,7 @@ private function getMatchablePropertyFromPrintRequest() { // label if ( $this->mPrintRequest->getLabel() !== '' && $this->mPrintRequest->getLabel() !== $property->getLabel() ) { return $this->jsonResponseParser->findPropertyFromInMemoryExternalRepositoryCache( - DIProperty::newFromUserLabel( $this->mPrintRequest->getLabel() ) + Property::newFromUserLabel( $this->mPrintRequest->getLabel() ) ); } diff --git a/src/ByHttpRequest/JsonResponseParser.php b/src/ByHttpRequest/JsonResponseParser.php index 5acff54..a387188 100644 --- a/src/ByHttpRequest/JsonResponseParser.php +++ b/src/ByHttpRequest/JsonResponseParser.php @@ -3,8 +3,8 @@ namespace SEQL\ByHttpRequest; use SEQL\DataValueDeserializer; -use SMW\DIProperty; -use SMW\DIWikiPage; +use SMW\DataItems\Property; +use SMW\DataItems\WikiPage; /** * @license GPL-2.0-or-later @@ -57,11 +57,11 @@ public function __construct( DataValueDeserializer $dataValueDeserializer ) { /** * @since 1.0 * - * @param DIProperty $property + * @param Property $property * - * @return DIProperty + * @return Property */ - public function findPropertyFromInMemoryExternalRepositoryCache( DIProperty $property ) { + public function findPropertyFromInMemoryExternalRepositoryCache( Property $property ) { $key = $property->getKey(); if ( $this->responsePropertyList->hasProperty( $key ) ) { @@ -113,12 +113,12 @@ public function getRawResponseResult() { /** * @since 1.0 * - * @param DIWikiPage $subject - * @param DIProperty $property + * @param WikiPage $subject + * @param Property $property * * @return array */ - public function getPropertyValuesFor( DIWikiPage $subject, DIProperty $property ) { + public function getPropertyValuesFor( WikiPage $subject, Property $property ) { $hash = $subject->getHash(); $key = $this->responsePropertyList->findPropertyKey( $property->getKey() ); @@ -183,7 +183,7 @@ private function addResultsToPrintoutList( $k, $value ) { } private function addPropertyValues( $hash, $pk, $pvalues ) { - $property = DIProperty::newFromUserLabel( $pk ); + $property = Property::newFromUserLabel( $pk ); $pk = $property->getKey(); if ( !$this->responsePropertyList->hasProperty( $pk ) ) { diff --git a/src/ByHttpRequest/QueryResult.php b/src/ByHttpRequest/QueryResult.php index 304e791..bae021a 100644 --- a/src/ByHttpRequest/QueryResult.php +++ b/src/ByHttpRequest/QueryResult.php @@ -2,8 +2,8 @@ namespace SEQL\ByHttpRequest; +use SMW\Formatters\Infolink; use SMW\Query\QueryResult as RootQueryResult; -use SMWInfolink as Infolink; /** * @license GPL-2.0-or-later @@ -48,7 +48,7 @@ public function setJsonResponseParser( JsonResponseParser $jsonResponseParser ) * * @return array */ - public function toArray() { + public function toArray(): array { return $this->jsonResponseParser->getRawResponseResult(); } @@ -59,7 +59,7 @@ public function toArray() { * * @return array */ - public function serializeToArray() { + public function serializeToArray(): array { return $this->toArray(); } @@ -68,7 +68,7 @@ public function serializeToArray() { * * @return CannedResultArray[]|false */ - public function getNext() { + public function getNext(): false|array { $page = current( $this->mResults ); next( $this->mResults ); @@ -86,11 +86,15 @@ public function getNext() { } /** + * @see QueryResult::getQueryLink + * * @since 1.0 * - * @return SMWInfolink + * @param string|false $caption + * + * @return Infolink */ - public function getLink() { + public function getQueryLink( $caption = false ): Infolink { $params = [ trim( $this->getQuery()->getQueryString() ?? '' ) ]; foreach ( $this->getQuery()->getExtraPrintouts() as $printout ) { diff --git a/src/ByHttpRequest/QueryResultFetcher.php b/src/ByHttpRequest/QueryResultFetcher.php index c1d562a..2e80836 100644 --- a/src/ByHttpRequest/QueryResultFetcher.php +++ b/src/ByHttpRequest/QueryResultFetcher.php @@ -5,7 +5,7 @@ use Onoi\HttpRequest\HttpRequestFactory; use SEQL\QueryEncoder; use SEQL\QueryResultFactory; -use SMWQuery as Query; +use SMW\Query\Query; /** * @license GPL-2.0-or-later diff --git a/src/ByHttpRequest/ResponsePropertyList.php b/src/ByHttpRequest/ResponsePropertyList.php index 3cfe2d6..3fd2809 100644 --- a/src/ByHttpRequest/ResponsePropertyList.php +++ b/src/ByHttpRequest/ResponsePropertyList.php @@ -3,7 +3,7 @@ namespace SEQL\ByHttpRequest; use RuntimeException; -use SMW\DIProperty; +use SMW\DataItems\Property; /** * @license GPL-2.0-or-later @@ -40,7 +40,7 @@ public function __construct( $querySource ) { /** * @since 1.0 * - * @return DIProperty[] + * @return Property[] */ public function getPropertyList() { return $this->propertyList; @@ -77,7 +77,7 @@ public function hasProperty( $key ) { * * @param string $key * - * @return DIProperty|null + * @return Property|null */ public function getProperty( $key ) { $key = $this->findPropertyKey( $key ); @@ -100,7 +100,7 @@ public function addToPropertyList( array $value ) { } if ( $value['mode'] == 0 ) { - $property = new DIProperty( '_INST' ); + $property = new Property( '_INST' ); $this->internalLabelToKeyMap[$value['label']] = $property->getKey(); } else { $property = $this->newProperty( $value ); @@ -115,13 +115,13 @@ public function addToPropertyList( array $value ) { } private function newProperty( $value ) { - $property = DIProperty::newFromUserLabel( $value['label'] ); + $property = Property::newFromUserLabel( $value['label'] ); if ( $property->isUserDefined() ) { - return $property->setPropertyTypeId( $value['typeid'] ); + return $property->setPropertyValueType( $value['typeid'] ); } - if ( $property->findPropertyTypeID() === $value['typeid'] ) { + if ( $property->findPropertyValueType() === $value['typeid'] ) { return $property; } diff --git a/src/ByHttpRequestQueryLookup.php b/src/ByHttpRequestQueryLookup.php index e91e9fd..383040d 100644 --- a/src/ByHttpRequestQueryLookup.php +++ b/src/ByHttpRequestQueryLookup.php @@ -3,14 +3,15 @@ namespace SEQL; use MediaWiki\MediaWikiServices; +use Onoi\Cache\CacheFactory as OnoiCacheFactory; use Onoi\HttpRequest\HttpRequestFactory; use SEQL\ByHttpRequest\JsonResponseParser; use SEQL\ByHttpRequest\QueryResultFetcher; use SMW\CacheFactory; +use SMW\Query\Query; use SMW\Query\QueryResult; use SMW\Services\ServicesFactory as ApplicationFactory; use SMW\SQLStore\SQLStore; -use SMWQuery as Query; /** * @license GPL-2.0-or-later @@ -68,7 +69,7 @@ protected function tryToMatchInterwikiFor( Query $query ) { protected function fetchQueryResultFor( Query $query, $interwiki, $credentials = false ) { $queryResultFetcher = new QueryResultFetcher( - new HttpRequestFactory( $this->getCacheFactory()->newMediaWikiCompositeCache( $GLOBALS['seqlgHttpResponseCacheType'] ) ), + new HttpRequestFactory( $this->newHttpResponseCache( $GLOBALS['seqlgHttpResponseCacheType'] ) ), $this->queryResultFactory, new JsonResponseParser( new DataValueDeserializer( $query->getQuerySource() ) ), $credentials @@ -91,4 +92,19 @@ private function getCacheFactory() { return $this->cacheFactory; } + /** + * Builds the Onoi cache used by the HTTP request layer. Replaces the + * removed SMW `CacheFactory::newMediaWikiCompositeCache` by composing the + * equivalent Onoi caches directly on top of a MediaWiki BagOStuff. + */ + private function newHttpResponseCache( $cacheType ) { + $objectCacheFactory = MediaWikiServices::getInstance()->getObjectCacheFactory(); + $cacheFactory = OnoiCacheFactory::getInstance(); + + return $cacheFactory->newCompositeCache( [ + $cacheFactory->newFixedInMemoryCache( 500 ), + $cacheFactory->newMediaWikiCache( $objectCacheFactory->getInstance( $cacheType ) ) + ] ); + } + } diff --git a/src/DataValueDeserializer.php b/src/DataValueDeserializer.php index f1e9890..8e56266 100644 --- a/src/DataValueDeserializer.php +++ b/src/DataValueDeserializer.php @@ -2,13 +2,13 @@ namespace SEQL; +use SMW\DataItems\Blob; +use SMW\DataItems\Container; +use SMW\DataItems\Property; +use SMW\DataItems\Time; +use SMW\DataItems\WikiPage; use SMW\DataModel\ContainerSemanticData; use SMW\DataValueFactory; -use SMW\DIProperty; -use SMW\DIWikiPage; -use SMWDIBlob as DIBlob; -use SMWDIContainer as DIContainer; -use SMWDITime as DITime; /** * @license GPL-2.0-or-later @@ -50,25 +50,25 @@ public function getQuerySource() { /** * @since 1.0 * - * @param DIProperty $property + * @param Property $property * @param array|string $value * * @return DataValue */ - public function newDataValueFrom( DIProperty $property, $value ) { + public function newDataValueFrom( Property $property, $value ) { $dv = null; $propertyList = []; - if ( $property->findPropertyTypeId() === '_wpg' || isset( $value['fulltext'] ) ) { + if ( $property->findPropertyValueType() === '_wpg' || isset( $value['fulltext'] ) ) { $dv = $this->newDataValueFromDataItem( $property, $this->newDiWikiPage( $value ) ); - } elseif ( strpos( $property->findPropertyTypeId(), '_rec' ) !== false ) { + } elseif ( strpos( $property->findPropertyValueType(), '_rec' ) !== false ) { $dv = $this->newDataValueFromDataItem( $property, $this->newDiContainerOnRecordType( $value, $propertyList ) ); $dv->setFieldProperties( $propertyList ); - } elseif ( $property->findPropertyTypeId() === '_dat' ) { + } elseif ( $property->findPropertyValueType() === '_dat' ) { $dv = $this->newDataValueFromDataItem( $property, $this->newDiTime( $value ) ); - } elseif ( strpos( $property->findPropertyTypeId(), '_txt' ) !== false ) { + } elseif ( strpos( $property->findPropertyValueType(), '_txt' ) !== false ) { $dv = $this->newDataValueFromDataItem( $property, $this->newDiBlob( $value ) ); - } elseif ( $property->findPropertyTypeId() === '_qty' ) { + } elseif ( $property->findPropertyValueType() === '_qty' ) { $dv = $this->newDataValueFromPropertyObject( $property, $value['value'] . ' ' . $value['unit'] ); } @@ -84,7 +84,7 @@ public function newDataValueFrom( DIProperty $property, $value ) { * * @param array $value * - * @return DIWikiPage|false + * @return WikiPage|false */ public function newDiWikiPage( array $value ) { if ( !isset( $value['namespace'] ) || !isset( $value['fulltext'] ) ) { @@ -99,12 +99,12 @@ public function newDiWikiPage( array $value ) { $title = \Title::newFromText( $this->querySource . ':' . str_replace( " ", "_", $value['fulltext'] ), $ns ); - return DIWikiPage::newFromTitle( $title ); + return WikiPage::newFromTitle( $title ); } private function newDiTime( $value ) { if ( isset( $value['raw'] ) ) { - return DITime::doUnserialize( $value['raw'] ); + return Time::doUnserialize( $value['raw'] ); } // < 0.7 API format @@ -112,21 +112,21 @@ private function newDiTime( $value ) { // doesn't sent a raw format // return 9999 BC to indicate that we hit a bounds with the timespamp try { - $dataItem = DITime::newFromTimestamp( $value ); + $dataItem = Time::newFromTimestamp( $value ); } catch ( \Exception $e ) { - $dataItem = DITime::doUnserialize( '2/-9999' ); + $dataItem = Time::doUnserialize( '2/-9999' ); } return $dataItem; } private function newDiBlob( $value ) { - return new DIBlob( $this->embeddedLinksReplacer->replace( $value ) ); + return new Blob( $this->embeddedLinksReplacer->replace( $value ) ); } private function newDataValueFromPropertyObject( $property, $value ) { try { - $dv = DataValueFactory::newPropertyObjectValue( $property, $value ); + $dv = DataValueFactory::getInstance()->newDataValueByProperty( $property, $value ); } catch ( \Exception $e ) { $dv = false; } @@ -140,7 +140,7 @@ private function newDataValueFromDataItem( $property, $dataItem = false ) { } try { - $dv = DataValueFactory::newDataItemValue( $dataItem, $property ); + $dv = DataValueFactory::getInstance()->newDataValueByItem( $dataItem, $property ); } catch ( \Exception $e ) { $dv = false; } @@ -153,9 +153,9 @@ private function newDiContainerOnRecordType( array $value, &$propertyList ) { $semanticData = ContainerSemanticData::makeAnonymousContainer(); foreach ( $value as $recValue ) { - $recordProperty = DIProperty::newFromUserLabel( $recValue['label'] ); + $recordProperty = Property::newFromUserLabel( $recValue['label'] ); $recordProperty->setInterwiki( $this->querySource ); - $recordProperty->setPropertyTypeId( $recValue['typeid'] ); + $recordProperty->setPropertyValueType( $recValue['typeid'] ); $propertyList[] = $recordProperty; foreach ( $recValue['item'] as $item ) { @@ -169,7 +169,7 @@ private function newDiContainerOnRecordType( array $value, &$propertyList ) { } } - return new DIContainer( $semanticData ); + return new Container( $semanticData ); } } diff --git a/src/QueryEncoder.php b/src/QueryEncoder.php index 504226d..965c870 100644 --- a/src/QueryEncoder.php +++ b/src/QueryEncoder.php @@ -2,7 +2,7 @@ namespace SEQL; -use SMWQuery as Query; +use SMW\Query\Query; /** * @license GPL-2.0-or-later @@ -38,7 +38,7 @@ public static function encode( Query $query ) { $serialized['parameters'] = [ 'limit=' . $query->getLimit(), 'offset=' . $query->getOffset(), - 'mainlabel=' . $query->getMainlabel(), + 'mainlabel=' . $query->getMainLabel(), // 'source=' . $query->getQuerySource() ]; diff --git a/src/QueryResultFactory.php b/src/QueryResultFactory.php index c96df8d..a34500c 100644 --- a/src/QueryResultFactory.php +++ b/src/QueryResultFactory.php @@ -4,9 +4,9 @@ use SEQL\ByHttpRequest\JsonResponseParser; use SEQL\ByHttpRequest\QueryResult as ByHttpRequestQueryResult; +use SMW\Query\Query; use SMW\Query\QueryResult; use SMW\Store; -use SMWQuery as Query; /** * @license GPL-2.0-or-later diff --git a/tests/phpunit/Integration/ByHttpRequest/JsonParseOnFauxHttpResponseTest.php b/tests/phpunit/Integration/ByHttpRequest/JsonParseOnFauxHttpResponseTest.php index c37ab82..b62128e 100644 --- a/tests/phpunit/Integration/ByHttpRequest/JsonParseOnFauxHttpResponseTest.php +++ b/tests/phpunit/Integration/ByHttpRequest/JsonParseOnFauxHttpResponseTest.php @@ -5,8 +5,8 @@ use SEQL\ByHttpRequest\JsonResponseParser; use SEQL\DataValueDeserializer; use SEQL\HookRegistry; -use SMW\DIProperty; -use SMW\DIWikiPage; +use SMW\DataItems\Property; +use SMW\DataItems\WikiPage; use SMW\Tests\Utils\UtilityFactory; /** @@ -69,7 +69,7 @@ public function testQueryResultFetcherFromCannedJsonResponse( $file ) { private function assertSubjectList( $expectedSubjectList, $subjectList ) { foreach ( $subjectList as $subject ) { foreach ( $expectedSubjectList as $key => $sub ) { - $sub = DIWikiPage::doUnserialize( str_replace( " ", "_", $sub ) ); + $sub = WikiPage::doUnserialize( str_replace( " ", "_", $sub ) ); if ( $subject->equals( $sub ) ) { unset( $expectedSubjectList[$key] ); break; @@ -86,7 +86,7 @@ private function assertSubjectList( $expectedSubjectList, $subjectList ) { private function assertPrintRequestPropertyList( $expectedPropertyList, $printRequestPropertyList ) { foreach ( $printRequestPropertyList as $property ) { foreach ( $expectedPropertyList as $key => $prop ) { - $prop = new DIProperty( str_replace( " ", "_", $prop ) ); + $prop = new Property( str_replace( " ", "_", $prop ) ); if ( $property->equals( $prop ) ) { unset( $expectedPropertyList[$key] ); break; diff --git a/tests/phpunit/Unit/ByHttpRequest/CannedResultArrayTest.php b/tests/phpunit/Unit/ByHttpRequest/CannedResultArrayTest.php index 7272896..dcc537f 100644 --- a/tests/phpunit/Unit/ByHttpRequest/CannedResultArrayTest.php +++ b/tests/phpunit/Unit/ByHttpRequest/CannedResultArrayTest.php @@ -3,9 +3,9 @@ namespace SEQL\ByHttpRequest\Tests; use SEQL\ByHttpRequest\CannedResultArray; -use SMW\DIProperty; -use SMW\DIWikiPage; -use SMWDINumber as DINumber; +use SMW\DataItems\Number; +use SMW\DataItems\Property; +use SMW\DataItems\WikiPage; /** * @covers \SEQL\ByHttpRequest\CannedResultArray @@ -33,12 +33,12 @@ public function testCanConstruct() { $this->assertInstanceOf( '\SEQL\ByHttpRequest\CannedResultArray', - new CannedResultArray( new DIWikiPage( 'Foo', NS_MAIN ), $printRequest, $this->jsonResponseParser ) + new CannedResultArray( new WikiPage( 'Foo', NS_MAIN ), $printRequest, $this->jsonResponseParser ) ); } public function testGetResultSubject() { - $subject = new DIWikiPage( 'Foo', NS_MAIN ); + $subject = new WikiPage( 'Foo', NS_MAIN ); $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' ) ->disableOriginalConstructor() @@ -57,7 +57,7 @@ public function testGetResultSubject() { } public function testGetContentForMode_PRINT_THIS() { - $subject = new DIWikiPage( 'Foo', NS_MAIN ); + $subject = new WikiPage( 'Foo', NS_MAIN ); $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' ) ->disableOriginalConstructor() @@ -80,14 +80,14 @@ public function testGetContentForMode_PRINT_THIS() { } public function testGetContentForMode_PRINT_CCAT() { - $subject = new DIWikiPage( 'Foo', NS_MAIN ); - $category = new DIWikiPage( 'Bar', NS_CATEGORY, 'mw-foo' ); + $subject = new WikiPage( 'Foo', NS_MAIN ); + $category = new WikiPage( 'Bar', NS_CATEGORY, 'mw-foo' ); $this->jsonResponseParser->expects( $this->any() ) ->method( 'getPropertyValuesFor' ) ->with( $subject, - new DIProperty( '_INST' ) ) + new Property( '_INST' ) ) ->willReturn( [ $category ] ); $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' ) @@ -111,8 +111,8 @@ public function testGetContentForMode_PRINT_CCAT() { } public function testGetContentOnDifferentPropertyLabelForMode_PRINT_PROP() { - $subject = new DIWikiPage( 'Foo', NS_MAIN ); - $dataItem = new DINumber( 1001 ); + $subject = new WikiPage( 'Foo', NS_MAIN ); + $dataItem = new Number( 1001 ); $propertyValue = $this->getMockBuilder( '\SMW\DataValues\PropertyValue' ) ->disableOriginalConstructor() @@ -124,9 +124,9 @@ public function testGetContentOnDifferentPropertyLabelForMode_PRINT_PROP() { $propertyValue->expects( $this->any() ) ->method( 'getDataItem' ) - ->willReturn( new DIProperty( 'ABC' ) ); + ->willReturn( new Property( 'ABC' ) ); - $dataValue = $this->getMockBuilder( '\SMWDataValue' ) + $dataValue = $this->getMockBuilder( '\SMW\DataValues\DataValue' ) ->disableOriginalConstructor() ->onlyMethods( [ 'getDataItem' ] ) ->getMockForAbstractClass(); @@ -139,14 +139,14 @@ public function testGetContentOnDifferentPropertyLabelForMode_PRINT_PROP() { ->method( 'getPropertyValuesFor' ) ->with( $subject, - new DIProperty( 'isPropertyFromInMemoryExternalRepositoryCache' ) ) + new Property( 'isPropertyFromInMemoryExternalRepositoryCache' ) ) ->willReturn( [ $dataValue ] ); $this->jsonResponseParser->expects( $this->any() ) ->method( 'findPropertyFromInMemoryExternalRepositoryCache' ) ->with( - new DIProperty( 'withDifferentPropertyLabel' ) ) - ->willReturn( new DIProperty( 'isPropertyFromInMemoryExternalRepositoryCache' ) ); + new Property( 'withDifferentPropertyLabel' ) ) + ->willReturn( new Property( 'isPropertyFromInMemoryExternalRepositoryCache' ) ); $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' ) ->disableOriginalConstructor() @@ -177,12 +177,12 @@ public function testGetContentOnDifferentPropertyLabelForMode_PRINT_PROP() { } public function testGetContentOnQuantityTypeWithSameLabelForMode_PRINT_PROP() { - $subject = new DIWikiPage( 'Foo', NS_MAIN ); + $subject = new WikiPage( 'Foo', NS_MAIN ); - $property = new DIProperty( 'QuantityType' ); - $property->setPropertyTypeId( '_qty' ); + $property = new Property( 'QuantityType' ); + $property->setPropertyValueType( '_qty' ); - $dataItem = new DINumber( 1001 ); + $dataItem = new Number( 1001 ); $propertyValue = $this->getMockBuilder( '\SMW\DataValues\PropertyValue' ) ->disableOriginalConstructor() @@ -194,9 +194,9 @@ public function testGetContentOnQuantityTypeWithSameLabelForMode_PRINT_PROP() { $propertyValue->expects( $this->any() ) ->method( 'getDataItem' ) - ->willReturn( new DIProperty( 'ABC' ) ); + ->willReturn( new Property( 'ABC' ) ); - $dataValue = $this->getMockBuilder( '\SMWDataValue' ) + $dataValue = $this->getMockBuilder( '\SMW\DataValues\DataValue' ) ->disableOriginalConstructor() ->onlyMethods( [ 'getDataItem' ] ) ->getMockForAbstractClass(); @@ -215,7 +215,7 @@ public function testGetContentOnQuantityTypeWithSameLabelForMode_PRINT_PROP() { $this->jsonResponseParser->expects( $this->any() ) ->method( 'findPropertyFromInMemoryExternalRepositoryCache' ) ->with( - new DIProperty( 'ABC' ) ) + new Property( 'ABC' ) ) ->willReturn( $property ); $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' ) @@ -247,7 +247,7 @@ public function testGetContentOnQuantityTypeWithSameLabelForMode_PRINT_PROP() { } public function testOptions() { - $subject = new DIWikiPage( 'Foo', NS_MAIN ); + $subject = new WikiPage( 'Foo', NS_MAIN ); $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' ) ->disableOriginalConstructor() @@ -285,7 +285,7 @@ private function assertDataItem( $dataItem, $instance ) { $instance->reset(); $this->assertInstanceOf( - '\SMWDataValue', + '\SMW\DataValues\DataValue', $instance->getNextDataValue() ); diff --git a/tests/phpunit/Unit/ByHttpRequest/JsonResponseParserTest.php b/tests/phpunit/Unit/ByHttpRequest/JsonResponseParserTest.php index 0ef6410..876c38f 100644 --- a/tests/phpunit/Unit/ByHttpRequest/JsonResponseParserTest.php +++ b/tests/phpunit/Unit/ByHttpRequest/JsonResponseParserTest.php @@ -3,7 +3,7 @@ namespace SEQL\ByHttpRequest\Tests; use SEQL\ByHttpRequest\JsonResponseParser; -use SMW\DIProperty; +use SMW\DataItems\Property; /** * @covers \SEQL\ByHttpRequest\JsonResponseParser @@ -74,8 +74,8 @@ public function testDoParseForRedirect() { 'results' => [] ]; - $property = new DIProperty( 'Foo' ); - $property->setPropertyTypeId( '_wpg' ); + $property = new Property( 'Foo' ); + $property->setPropertyValueType( '_wpg' ); $property->setInterwiki( 'abc' ); $dataValueDeserializer = $this->getMockBuilder( '\SEQL\DataValueDeserializer' ) @@ -97,12 +97,12 @@ public function testDoParseForRedirect() { $this->assertEquals( $property, - $instance->findPropertyFromInMemoryExternalRepositoryCache( new DIProperty( 'Bar' ) ) + $instance->findPropertyFromInMemoryExternalRepositoryCache( new Property( 'Bar' ) ) ); $this->assertEquals( $property, - $instance->findPropertyFromInMemoryExternalRepositoryCache( new DIProperty( 'Foo' ) ) + $instance->findPropertyFromInMemoryExternalRepositoryCache( new Property( 'Foo' ) ) ); } @@ -142,7 +142,7 @@ public function resultProvider() { ] ], true, - new DIProperty( '_INST' ) + new Property( '_INST' ) ]; # 3 @@ -162,7 +162,7 @@ public function resultProvider() { 'results' => [] ], false, - new DIProperty( '_INST' ) + new Property( '_INST' ) ]; return $provider; diff --git a/tests/phpunit/Unit/ByHttpRequest/QueryResultFetcherTest.php b/tests/phpunit/Unit/ByHttpRequest/QueryResultFetcherTest.php index a7cde8f..55daf6c 100644 --- a/tests/phpunit/Unit/ByHttpRequest/QueryResultFetcherTest.php +++ b/tests/phpunit/Unit/ByHttpRequest/QueryResultFetcherTest.php @@ -4,7 +4,7 @@ use SEQL\ByHttpRequest\QueryResultFetcher; use SEQL\QueryResultFactory; -use SMW\DIProperty; +use SMW\DataItems\Property; /** * @covers \SEQL\ByHttpRequest\QueryResultFetcher @@ -74,7 +74,7 @@ public function testFetchEmptyQueryResult() { ->method( 'getPrintrequests' ) ->willReturn( [ $printRequest ] ); - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); @@ -112,7 +112,7 @@ public function testResetOfPrintRequest() { $dataValue->expects( $this->once() ) ->method( 'getDataItem' ) - ->willReturn( new DIProperty( 'Foo' ) ); + ->willReturn( new Property( 'Foo' ) ); $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' ) ->disableOriginalConstructor() @@ -134,7 +134,7 @@ public function testResetOfPrintRequest() { ->method( 'getPrintrequests' ) ->willReturn( [ $printRequest ] ); - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); @@ -182,7 +182,7 @@ public function testFetchQueryResultWithResponseCache() { ->method( 'getPrintrequests' ) ->willReturn( [ $printRequest ] ); - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); @@ -248,7 +248,7 @@ public function testHttpRequestToReturnWithError() { ->method( 'getPrintrequests' ) ->willReturn( [ $printRequest ] ); - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); @@ -303,7 +303,7 @@ public function testHttpRequestToReturnWithValidJson() { ->method( 'getPrintrequests' ) ->willReturn( [ $printRequest ] ); - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); diff --git a/tests/phpunit/Unit/ByHttpRequest/QueryResultTest.php b/tests/phpunit/Unit/ByHttpRequest/QueryResultTest.php index 992c01d..f41a87e 100644 --- a/tests/phpunit/Unit/ByHttpRequest/QueryResultTest.php +++ b/tests/phpunit/Unit/ByHttpRequest/QueryResultTest.php @@ -3,7 +3,7 @@ namespace SEQL\ByHttpRequest\Tests; use SEQL\ByHttpRequest\QueryResult; -use SMW\DIWikiPage; +use SMW\DataItems\WikiPage; /** * @covers \SEQL\ByHttpRequest\QueryResult @@ -25,7 +25,7 @@ protected function setUp(): void { } public function testCanConstruct() { - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); @@ -43,7 +43,7 @@ public function testGetNext() { ->disableOriginalConstructor() ->getMock(); - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); @@ -56,7 +56,7 @@ public function testGetNext() { ]; $results = [ - new DIWikiPage( 'Foo', NS_MAIN ) + new WikiPage( 'Foo', NS_MAIN ) ]; $instance = new QueryResult( $printRequests, $query, $results, $this->store, false ); @@ -75,7 +75,7 @@ public function testToArray() { 'Foo' ]; - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); @@ -108,7 +108,7 @@ public function testToArray() { ); } - public function testGetLink() { + public function testGetQueryLink() { $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' ) ->disableOriginalConstructor() ->getMock(); @@ -117,7 +117,7 @@ public function testGetLink() { ->method( 'getSerialisation' ) ->willReturn( '?ABC' ); - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); @@ -132,8 +132,8 @@ public function testGetLink() { $instance->setRemoteTargetUrl( 'http://example.org:8080' ); $this->assertInstanceOf( - '\SMWInfolink', - $instance->getLink() + '\SMW\Formatters\Infolink', + $instance->getQueryLink() ); } diff --git a/tests/phpunit/Unit/ByHttpRequest/ResponsePropertyListTest.php b/tests/phpunit/Unit/ByHttpRequest/ResponsePropertyListTest.php index 14afe80..c1eb25e 100644 --- a/tests/phpunit/Unit/ByHttpRequest/ResponsePropertyListTest.php +++ b/tests/phpunit/Unit/ByHttpRequest/ResponsePropertyListTest.php @@ -3,7 +3,7 @@ namespace SEQL\ByHttpRequest\Tests; use SEQL\ByHttpRequest\ResponsePropertyList; -use SMW\DIProperty; +use SMW\DataItems\Property; /** * @covers \SEQL\ByHttpRequest\ResponsePropertyList @@ -28,7 +28,7 @@ public function testGetPropertyForCategory() { 'label' => 'Category', 'mode' => 0 ]; - $property = new DIProperty( '_INST' ); + $property = new Property( '_INST' ); $property->setInterwiki( 'abc' ); $instance = new ResponsePropertyList( 'abc' ); @@ -50,9 +50,9 @@ public function testGetPropertyForRedirectedProperty() { 'label' => 'Foo', 'mode' => 2, 'redi' => 'was redirect from Bar', 'typeid' => '_wpg' ]; - $property = new DIProperty( 'Foo' ); + $property = new Property( 'Foo' ); $property->setInterwiki( 'abc' ); - $property->setPropertyTypeId( '_wpg' ); + $property->setPropertyValueType( '_wpg' ); $instance = new ResponsePropertyList( 'abc' ); $instance->addToPropertyList( $value ); diff --git a/tests/phpunit/Unit/ByHttpRequestQueryLookupTest.php b/tests/phpunit/Unit/ByHttpRequestQueryLookupTest.php index 8aab333..29f8973 100644 --- a/tests/phpunit/Unit/ByHttpRequestQueryLookupTest.php +++ b/tests/phpunit/Unit/ByHttpRequestQueryLookupTest.php @@ -2,7 +2,7 @@ namespace SEQL\Tests; -use SMWQuery as Query; +use SMW\Query\Query; /** * @covers \SEQL\ByHttpRequestQueryLookup @@ -31,7 +31,7 @@ public function testGetQueryResult() { ->disableOriginalConstructor() ->getMockForAbstractClass(); - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); @@ -55,7 +55,7 @@ public function testGetEmptyQueryResult_MODE_DEBUG() { ->disableOriginalConstructor() ->getMockForAbstractClass(); - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); @@ -88,7 +88,7 @@ public function testGetQueryResultForSimulatedInterwikiMatch() { ->disableOriginalConstructor() ->getMockForAbstractClass(); - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); diff --git a/tests/phpunit/Unit/DataValueDeserializerTest.php b/tests/phpunit/Unit/DataValueDeserializerTest.php index a8a5048..5f5d36b 100644 --- a/tests/phpunit/Unit/DataValueDeserializerTest.php +++ b/tests/phpunit/Unit/DataValueDeserializerTest.php @@ -3,9 +3,9 @@ namespace SEQL\Tests; use SEQL\DataValueDeserializer; -use SMW\DIProperty; -use SMW\DIWikiPage; -use SMWDITime as DITime; +use SMW\DataItems\Property; +use SMW\DataItems\Time; +use SMW\DataItems\WikiPage; /** * @covers \SEQL\DataValueDeserializer @@ -34,7 +34,7 @@ public function testNewDiWikiPage() { ]; $this->assertEquals( - new DIWikiPage( 'Foo:abc_def', NS_MAIN ), + new WikiPage( 'Foo:abc_def', NS_MAIN ), $instance->newDiWikiPage( $value ) ); } @@ -50,11 +50,11 @@ public function testTryNewDiWikiPageForInvalidSeralization() { public function testNewTimeValueForOutOfRangeTimestamp() { $instance = new DataValueDeserializer( 'foo' ); - $property = new DIProperty( 'Bar' ); - $property->setPropertyTypeId( '_dat' ); + $property = new Property( 'Bar' ); + $property->setPropertyValueType( '_dat' ); $this->assertNotEquals( - DITime::doUnserialize( '2/-200' ), + Time::doUnserialize( '2/-200' ), $instance->newDataValueFrom( $property, '-2000101000000' ) ); } @@ -62,11 +62,11 @@ public function testNewTimeValueForOutOfRangeTimestamp() { public function testNewTimeValueForRawTimeFromat() { $instance = new DataValueDeserializer( 'foo' ); - $property = new DIProperty( 'Bar' ); - $property->setPropertyTypeId( '_dat' ); + $property = new Property( 'Bar' ); + $property->setPropertyValueType( '_dat' ); $this->assertEquals( - DITime::doUnserialize( '2/-200' ), + Time::doUnserialize( '2/-200' ), $instance->newDataValueFrom( $property, [ 'raw' => '2/-200' ] )->getDataItem() ); } @@ -74,8 +74,8 @@ public function testNewTimeValueForRawTimeFromat() { public function testNewRecordValue() { $instance = new DataValueDeserializer( 'foo' ); - $property = new DIProperty( 'Foo' ); - $property->setPropertyTypeId( '_rec' ); + $property = new Property( 'Foo' ); + $property->setPropertyValueType( '_rec' ); $item = [ 'namespace' => NS_MAIN, @@ -89,7 +89,7 @@ public function testNewRecordValue() { ]; $this->assertInstanceOf( - '\SMWRecordValue', + '\SMW\DataValues\RecordValue', $instance->newDataValueFrom( $property, $record ) ); } @@ -97,8 +97,8 @@ public function testNewRecordValue() { public function testTextValueWithEmbeddedLink() { $instance = new DataValueDeserializer( 'abc' ); - $property = new DIProperty( 'Bar' ); - $property->setPropertyTypeId( '_txt' ); + $property = new Property( 'Bar' ); + $property->setPropertyValueType( '_txt' ); $dataValue = $instance->newDataValueFrom( $property, 'Foo [[42]] bar' ); diff --git a/tests/phpunit/Unit/QueryEncoderTest.php b/tests/phpunit/Unit/QueryEncoderTest.php index 9df08db..1b6339c 100644 --- a/tests/phpunit/Unit/QueryEncoderTest.php +++ b/tests/phpunit/Unit/QueryEncoderTest.php @@ -19,7 +19,7 @@ class QueryEncoderTest extends \PHPUnit\Framework\TestCase { * @dataProvider queryElementProvider */ public function testEncode( $sortKeys, $extraPrintouts, $expectedEncode, $expectedRawEncode ) { - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); diff --git a/tests/phpunit/Unit/QueryResultFactoryTest.php b/tests/phpunit/Unit/QueryResultFactoryTest.php index 5758c0d..0262572 100644 --- a/tests/phpunit/Unit/QueryResultFactoryTest.php +++ b/tests/phpunit/Unit/QueryResultFactoryTest.php @@ -35,7 +35,7 @@ public function testNewEmptyQueryResult() { ->disableOriginalConstructor() ->getMockForAbstractClass(); - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); @@ -60,7 +60,7 @@ public function testNewByHttpLookupQueryResult() { ->disableOriginalConstructor() ->getMockForAbstractClass(); - $query = $this->getMockBuilder( '\SMWQuery' ) + $query = $this->getMockBuilder( '\SMW\Query\Query' ) ->disableOriginalConstructor() ->getMock(); diff --git a/tests/travis/install-mediawiki.sh b/tests/travis/install-mediawiki.sh deleted file mode 100644 index 4ad7955..0000000 --- a/tests/travis/install-mediawiki.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash -set -ex - -cd .. - -## Use sha (master@5cc1f1d) to download a particular commit to avoid breakages -## introduced by MediaWiki core -if [[ "$MW" == *@* ]] -then - arrMw=(${MW//@/ }) - MW=${arrMw[0]} - SOURCE=${arrMw[1]} -else - MW=$MW - SOURCE=$MW -fi - -wget https://github.com/wikimedia/mediawiki/archive/$SOURCE.tar.gz -O $MW.tar.gz - -tar -zxf $MW.tar.gz -mv mediawiki-* mw - -cd mw - -## MW 1.25+ requires Psr\Logger -if [ -f composer.json ] -then - composer self-update - composer install -fi - -if [ "$DB" == "postgres" ] -then - # See #458 - sudo /etc/init.d/postgresql stop - sudo /etc/init.d/postgresql start - - psql -c 'create database its_a_mw;' -U postgres - php maintenance/install.php --dbtype $DB --dbuser postgres --dbname its_a_mw --pass nyan TravisWiki admin --scriptpath /TravisWiki -else - mysql -e 'create database its_a_mw;' - php maintenance/install.php --dbtype $DB --dbuser root --dbname its_a_mw --dbpath $(pwd) --pass nyan TravisWiki admin --scriptpath /TravisWiki -fi diff --git a/tests/travis/install-semantic-external-query-lookup.sh b/tests/travis/install-semantic-external-query-lookup.sh deleted file mode 100644 index 3731e08..0000000 --- a/tests/travis/install-semantic-external-query-lookup.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/bash -set -ex - -BASE_PATH=$(pwd) -MW_INSTALL_PATH=$BASE_PATH/../mw - -# Run Composer installation from the MW root directory -function installToMediaWikiRoot { - echo -e "Running MW root composer install build on $TRAVIS_BRANCH \n" - - cd $MW_INSTALL_PATH - - if [ "$PHPUNIT" != "" ] - then - composer require 'phpunit/phpunit='$PHPUNIT --update-with-dependencies - else - composer require 'phpunit/phpunit=3.7.*' --update-with-dependencies - fi - - if [ "$SEQL" != "" ] - then - composer require 'mediawiki/semantic-external-query-lookup='$SEQL --update-with-dependencies - else - composer init --stability dev - composer require mediawiki/semantic-external-query-lookup "dev-master" --dev --update-with-dependencies - - cd extensions - cd SemanticExternalQueryLookup - - # Pull request number, "false" if it's not a pull request - # After the install via composer an additional get fetch is carried out to - # update th repository to make sure that the latests code changes are - # deployed for testing - if [ "$TRAVIS_PULL_REQUEST" != "false" ] - then - git fetch origin +refs/pull/"$TRAVIS_PULL_REQUEST"/merge: - git checkout -qf FETCH_HEAD - else - git fetch origin "$TRAVIS_BRANCH" - git checkout -qf FETCH_HEAD - fi - - cd ../.. - fi - - # Rebuild the class map for added classes during git fetch - composer dump-autoload -} - -function updateConfiguration { - - cd $MW_INSTALL_PATH - - # Site language - if [ "$SITELANG" != "" ] - then - echo '$wgLanguageCode = "'$SITELANG'";' >> LocalSettings.php - fi - - echo 'error_reporting(E_ALL| E_STRICT);' >> LocalSettings.php - echo 'ini_set("display_errors", 1);' >> LocalSettings.php - echo '$wgShowExceptionDetails = true;' >> LocalSettings.php - echo '$wgDevelopmentWarnings = true;' >> LocalSettings.php - echo "putenv( 'MW_INSTALL_PATH=$(pwd)' );" >> LocalSettings.php - - php maintenance/update.php --quick -} - -installToMediaWikiRoot -updateConfiguration diff --git a/tests/travis/run-tests.sh b/tests/travis/run-tests.sh deleted file mode 100644 index ce9ae05..0000000 --- a/tests/travis/run-tests.sh +++ /dev/null @@ -1,14 +0,0 @@ -#! /bin/bash -set -ex - -BASE_PATH=$(pwd) -MW_INSTALL_PATH=$BASE_PATH/../mw - -cd $MW_INSTALL_PATH/extensions/SemanticExternalQueryLookup - -if [ "$TYPE" == "coverage" ] -then - composer phpunit -- --coverage-clover $BASE_PATH/build/coverage.clover -else - composer phpunit -fi diff --git a/tests/travis/upload-coverage-report.sh b/tests/travis/upload-coverage-report.sh deleted file mode 100644 index aff95cf..0000000 --- a/tests/travis/upload-coverage-report.sh +++ /dev/null @@ -1,10 +0,0 @@ -#! /bin/bash -set -ex - -BASE_PATH=$(pwd) - -if [ "$TYPE" == "coverage" ] -then - wget https://scrutinizer-ci.com/ocular.phar - php ocular.phar code-coverage:upload --format=php-clover $BASE_PATH/build/coverage.clover -fi \ No newline at end of file From 0896156b8800a90d44cfcae1c487310204f5dc45 Mon Sep 17 00:00:00 2001 From: alistair3149 Date: Fri, 5 Jun 2026 17:43:51 -0400 Subject: [PATCH 2/6] Use MediaWiki core HttpRequestFactory Replace the mediawiki/http-request (Onoi\HttpRequest) dependency with MediaWiki core's HttpRequestFactory, dropping the external library and its onoi/cache transitive dependency. - Issue the ask and login requests through HttpRequestFactory::create() and read the body via getContent(), gating on execute()->isOK(). - Reimplement the HTTP response cache on a MediaWiki BagOStuff (ObjectCacheFactory keyed by $seqlgHttpResponseCacheType) instead of the Onoi composite cache. - Carry the authenticated session across requests with a core CookieJar instead of a curl cookie file. --- RELEASE-NOTES.md | 1 + composer.json | 3 +- src/ByHttpRequest/QueryResultFetcher.php | 157 +++++++++++------- src/ByHttpRequestQueryLookup.php | 22 +-- .../ByHttpRequest/QueryResultFetcherTest.php | 59 ++++--- 5 files changed, 144 insertions(+), 98 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index c9a5725..56d4faf 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -4,6 +4,7 @@ This file contains the RELEASE-NOTES of the Semantic External Query Lookup (a.k. - Added support for Semantic MediaWiki 7.0 - Raised the minimum requirements to MediaWiki 1.43 and Semantic MediaWiki 7.0 +- Replaced the `mediawiki/http-request` dependency with MediaWiki core's `HttpRequestFactory` ### 1.0.0 (2016-??-??) diff --git a/composer.json b/composer.json index 3011eff..979e54e 100644 --- a/composer.json +++ b/composer.json @@ -26,8 +26,7 @@ }, "require": { "php": ">=8.1.0", - "composer/installers": ">=1.0.1", - "mediawiki/http-request": "~2.0|~1.1" + "composer/installers": ">=1.0.1" }, "require-dev": { "mediawiki/mediawiki-codesniffer": "43.0.0", diff --git a/src/ByHttpRequest/QueryResultFetcher.php b/src/ByHttpRequest/QueryResultFetcher.php index 2e80836..928bb8a 100644 --- a/src/ByHttpRequest/QueryResultFetcher.php +++ b/src/ByHttpRequest/QueryResultFetcher.php @@ -2,10 +2,11 @@ namespace SEQL\ByHttpRequest; -use Onoi\HttpRequest\HttpRequestFactory; +use MediaWiki\Http\HttpRequestFactory; use SEQL\QueryEncoder; use SEQL\QueryResultFactory; use SMW\Query\Query; +use Wikimedia\ObjectCache\BagOStuff; /** * @license GPL-2.0-or-later @@ -20,6 +21,11 @@ class QueryResultFetcher { */ private $httpRequestFactory; + /** + * @var BagOStuff + */ + private $cache; + /** * @var QueryResultFactory */ @@ -43,12 +49,12 @@ class QueryResultFetcher { /** * @var string */ - private $httpResponseCachePrefix; + private $httpResponseCachePrefix = ''; /** * @var int */ - private $httpResponseCacheLifetime; + private $httpResponseCacheLifetime = 0; /** * @var array @@ -56,19 +62,22 @@ class QueryResultFetcher { private $credentials; /** - * @var string + * @var \CookieJar|null */ - private static $cookies; + private static $cookieJar; /** * @since 1.0 * * @param HttpRequestFactory $httpRequestFactory + * @param BagOStuff $cache * @param QueryResultFactory $queryResultFactory * @param JsonResponseParser $jsonResponseParser + * @param array|false $credentials */ - public function __construct( HttpRequestFactory $httpRequestFactory, QueryResultFactory $queryResultFactory, JsonResponseParser $jsonResponseParser, $credentials ) { + public function __construct( HttpRequestFactory $httpRequestFactory, BagOStuff $cache, QueryResultFactory $queryResultFactory, JsonResponseParser $jsonResponseParser, $credentials ) { $this->httpRequestFactory = $httpRequestFactory; + $this->cache = $cache; $this->queryResultFactory = $queryResultFactory; $this->jsonResponseParser = $jsonResponseParser; $this->credentials = $credentials; @@ -117,53 +126,65 @@ public function setHttpResponseCacheLifetime( $httpResponseCacheLifetime ) { * @param array $credentials */ public function doAuthenticateRemoteWiki( $credentials ) { - $cookiefile = 'seql_' . time(); - - $httpRequest = $this->httpRequestFactory->newCurlRequest(); - - $httpRequest->setOption( CURLOPT_FOLLOWLOCATION, true ); - - $httpRequest->setOption( CURLOPT_RETURNTRANSFER, true ); - $httpRequest->setOption( CURLOPT_FAILONERROR, true ); - $httpRequest->setOption( CURLOPT_SSL_VERIFYPEER, false ); - $httpRequest->setOption( CURLOPT_COOKIESESSION, true ); - $httpRequest->setOption( CURLOPT_COOKIEJAR, $cookiefile ); - $httpRequest->setOption( CURLOPT_COOKIEFILE, $cookiefile ); + $cookieJar = new \CookieJar(); + + // (1) Fetch a login token while collecting session cookies into the jar. + $tokenRequest = $this->httpRequestFactory->create( + $this->httpRequestEndpoint . '?action=query&format=json&meta=tokens&type=login', + [ + 'method' => 'GET', + 'followRedirects' => true, + 'sslVerifyCert' => false, + 'sslVerifyHost' => false, + ], + __METHOD__ + ); - $httpRequest->setOption( CURLOPT_URL, $this->httpRequestEndpoint . '?action=query&format=json&meta=tokens&type=login' ); + $tokenRequest->setCookieJar( $cookieJar ); - $response = $httpRequest->execute(); - $result = json_decode( $response, true ); + if ( !$tokenRequest->execute()->isOK() ) { + return; + } - if ( isset( $result['query']['tokens']['logintoken'] ) ) { + // Pull the same jar back so the Set-Cookie response is merged in. + $cookieJar = $tokenRequest->getCookieJar(); + $result = json_decode( $tokenRequest->getContent() ?? '', true ); - $token = $result['query']['tokens']['logintoken']; + if ( !isset( $result['query']['tokens']['logintoken'] ) ) { + return; + } - $httpRequest->setOption( CURLOPT_FOLLOWLOCATION, true ); - $httpRequest->setOption( CURLOPT_RETURNTRANSFER, true ); - $httpRequest->setOption( CURLOPT_FAILONERROR, true ); - $httpRequest->setOption( CURLOPT_SSL_VERIFYPEER, false ); - $httpRequest->setOption( CURLOPT_POST, true ); - $httpRequest->setOption( CURLOPT_URL, $this->httpRequestEndpoint ); - $httpRequest->setOption( CURLOPT_COOKIEJAR, $cookiefile ); - $httpRequest->setOption( CURLOPT_COOKIEFILE, $cookiefile ); + $token = $result['query']['tokens']['logintoken']; - $httpRequest->setOption( CURLOPT_POSTFIELDS, http_build_query( [ + // (2) Log in, reusing the same jar (sends the session cookies). + $loginRequest = $this->httpRequestFactory->create( + $this->httpRequestEndpoint, + [ + 'method' => 'POST', + 'sslVerifyCert' => false, + 'sslVerifyHost' => false, + 'postData' => http_build_query( [ 'action' => 'login', 'format' => 'json', 'lgname' => $credentials['username'], 'lgpassword' => $credentials['password'], 'lgtoken' => $token - ] ) - ); + ] ), + ], + __METHOD__ + ); - $response = $httpRequest->execute(); - $result = json_decode( $response, true ); + $loginRequest->setCookieJar( $cookieJar ); - if ( isset( $result['login']['lguserid'] ) ) { - self::$cookies = $cookiefile; - } + if ( !$loginRequest->execute()->isOK() ) { + return; + } + $cookieJar = $loginRequest->getCookieJar(); + $result = json_decode( $loginRequest->getContent() ?? '', true ); + + if ( isset( $result['login']['lguserid'] ) ) { + self::$cookieJar = $cookieJar; } } @@ -177,7 +198,7 @@ public function doAuthenticateRemoteWiki( $credentials ) { public function fetchQueryResult( Query $query ) { $this->doResetPrintRequestsToQuerySource( $query ); - if ( $this->credentials && !self::$cookies ) { + if ( $this->credentials && self::$cookieJar === null ) { $this->doAuthenticateRemoteWiki( $this->credentials ); } @@ -236,32 +257,54 @@ private function doResetPrintRequestsToQuerySource( $query ) { } private function doMakeHttpRequestFor( $query ) { - $httpRequest = $this->httpRequestFactory->newCachedCurlRequest(); + $url = $this->httpRequestEndpoint . '?action=ask&format=json&query=' . QueryEncoder::rawUrlEncode( $query ); - $httpRequest->setOption( ONOI_HTTP_REQUEST_RESPONSECACHE_TTL, $this->httpResponseCacheLifetime ); - $httpRequest->setOption( ONOI_HTTP_REQUEST_RESPONSECACHE_PREFIX, $this->httpResponseCachePrefix . ':seql:' ); + // Replicate the former ':seql:' response-cache namespace as key + // components; makeKey() is wiki-scoped and escapes the separators. + $key = $this->cache->makeKey( 'seql', $this->httpResponseCachePrefix, md5( $url ) ); - $httpRequest->setOption( CURLOPT_FOLLOWLOCATION, true ); + $isFromCache = true; + $response = $this->cache->get( $key ); - $httpRequest->setOption( CURLOPT_RETURNTRANSFER, true ); - $httpRequest->setOption( CURLOPT_FAILONERROR, true ); - $httpRequest->setOption( CURLOPT_SSL_VERIFYPEER, false ); + if ( $response === false ) { + $isFromCache = false; + $response = $this->fetchHttpResponse( $url ); - $httpRequest->setOption( CURLOPT_URL, $this->httpRequestEndpoint . '?action=ask&format=json&query=' . QueryEncoder::rawUrlEncode( $query ) ); + if ( is_string( $response ) && $response !== '' ) { + $this->cache->set( $key, $response, $this->httpResponseCacheLifetime ); + } + } - $httpRequest->setOption( CURLOPT_HTTPHEADER, [ - 'Accept: application/json', - 'Content-Type: application/json; charset=utf-8' - ] ); + return [ json_decode( $response ?? '', true ), $isFromCache ]; + } + + private function fetchHttpResponse( $url ) { + $request = $this->httpRequestFactory->create( + $url, + [ + 'method' => 'GET', + 'followRedirects' => true, + // Preserve the legacy "do not verify the remote certificate" + // behaviour. MediaWiki's Guzzle backend only skips verification + // when both flags are false, so both are required here. + 'sslVerifyCert' => false, + 'sslVerifyHost' => false, + ], + __METHOD__ + ); - if ( self::$cookies ) { - $httpRequest->setOption( CURLOPT_COOKIEJAR, self::$cookies ); - $httpRequest->setOption( CURLOPT_COOKIEFILE, self::$cookies ); + $request->setHeader( 'Accept', 'application/json' ); + $request->setHeader( 'Content-Type', 'application/json; charset=utf-8' ); + + if ( self::$cookieJar !== null ) { + $request->setCookieJar( self::$cookieJar ); } - $response = $httpRequest->execute(); + if ( !$request->execute()->isOK() ) { + return false; + } - return [ json_decode( $response ?? '', true ), $httpRequest->isFromCache() ]; + return $request->getContent(); } } diff --git a/src/ByHttpRequestQueryLookup.php b/src/ByHttpRequestQueryLookup.php index 383040d..88fad66 100644 --- a/src/ByHttpRequestQueryLookup.php +++ b/src/ByHttpRequestQueryLookup.php @@ -3,8 +3,6 @@ namespace SEQL; use MediaWiki\MediaWikiServices; -use Onoi\Cache\CacheFactory as OnoiCacheFactory; -use Onoi\HttpRequest\HttpRequestFactory; use SEQL\ByHttpRequest\JsonResponseParser; use SEQL\ByHttpRequest\QueryResultFetcher; use SMW\CacheFactory; @@ -68,8 +66,11 @@ protected function tryToMatchInterwikiFor( Query $query ) { } protected function fetchQueryResultFor( Query $query, $interwiki, $credentials = false ) { + $services = MediaWikiServices::getInstance(); + $queryResultFetcher = new QueryResultFetcher( - new HttpRequestFactory( $this->newHttpResponseCache( $GLOBALS['seqlgHttpResponseCacheType'] ) ), + $services->getHttpRequestFactory(), + $services->getObjectCacheFactory()->getInstance( $GLOBALS['seqlgHttpResponseCacheType'] ), $this->queryResultFactory, new JsonResponseParser( new DataValueDeserializer( $query->getQuerySource() ) ), $credentials @@ -92,19 +93,4 @@ private function getCacheFactory() { return $this->cacheFactory; } - /** - * Builds the Onoi cache used by the HTTP request layer. Replaces the - * removed SMW `CacheFactory::newMediaWikiCompositeCache` by composing the - * equivalent Onoi caches directly on top of a MediaWiki BagOStuff. - */ - private function newHttpResponseCache( $cacheType ) { - $objectCacheFactory = MediaWikiServices::getInstance()->getObjectCacheFactory(); - $cacheFactory = OnoiCacheFactory::getInstance(); - - return $cacheFactory->newCompositeCache( [ - $cacheFactory->newFixedInMemoryCache( 500 ), - $cacheFactory->newMediaWikiCache( $objectCacheFactory->getInstance( $cacheType ) ) - ] ); - } - } diff --git a/tests/phpunit/Unit/ByHttpRequest/QueryResultFetcherTest.php b/tests/phpunit/Unit/ByHttpRequest/QueryResultFetcherTest.php index 55daf6c..08f4c88 100644 --- a/tests/phpunit/Unit/ByHttpRequest/QueryResultFetcherTest.php +++ b/tests/phpunit/Unit/ByHttpRequest/QueryResultFetcherTest.php @@ -2,9 +2,11 @@ namespace SEQL\ByHttpRequest\Tests; +use MediaWiki\Status\Status; use SEQL\ByHttpRequest\QueryResultFetcher; use SEQL\QueryResultFactory; use SMW\DataItems\Property; +use Wikimedia\ObjectCache\HashBagOStuff; /** * @covers \SEQL\ByHttpRequest\QueryResultFetcher @@ -20,6 +22,7 @@ class QueryResultFetcherTest extends \PHPUnit\Framework\TestCase { private $store; private $httpRequest; private $httpRequestFactory; + private $cache; private $jsonResponseParser; protected function setUp(): void { @@ -27,18 +30,24 @@ protected function setUp(): void { ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\CachedCurlRequest' ) + $this->httpRequest = $this->getMockBuilder( '\MWHttpRequest' ) ->disableOriginalConstructor() ->getMock(); - $this->httpRequestFactory = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequestFactory' ) + $this->httpRequest->expects( $this->any() ) + ->method( 'execute' ) + ->willReturn( Status::newGood() ); + + $this->httpRequestFactory = $this->getMockBuilder( '\MediaWiki\Http\HttpRequestFactory' ) ->disableOriginalConstructor() ->getMock(); $this->httpRequestFactory->expects( $this->any() ) - ->method( 'newCachedCurlRequest' ) + ->method( 'create' ) ->willReturn( $this->httpRequest ); + $this->cache = new HashBagOStuff(); + $this->jsonResponseParser = $this->getMockBuilder( '\SEQL\ByHttpRequest\JsonResponseParser' ) ->disableOriginalConstructor() ->getMock(); @@ -51,7 +60,7 @@ public function testCanConstruct() { $this->assertInstanceOf( '\SEQL\ByHttpRequest\QueryResultFetcher', - new QueryResultFetcher( $this->httpRequestFactory, $queryResultFactory, $this->jsonResponseParser, [] ) + new QueryResultFetcher( $this->httpRequestFactory, $this->cache, $queryResultFactory, $this->jsonResponseParser, [] ) ); } @@ -92,6 +101,7 @@ public function testFetchEmptyQueryResult() { $instance = new QueryResultFetcher( $this->httpRequestFactory, + $this->cache, $queryResultFactory, $this->jsonResponseParser, [] @@ -152,6 +162,7 @@ public function testResetOfPrintRequest() { $instance = new QueryResultFetcher( $this->httpRequestFactory, + $this->cache, $queryResultFactory, $this->jsonResponseParser, [] @@ -163,7 +174,8 @@ public function testResetOfPrintRequest() { ); } - public function testFetchQueryResultWithResponseCache() { + public function testFetchQueryResultUsesResponseCache() { + $cache = new HashBagOStuff(); $queryResultFactory = new QueryResultFactory( $this->store ); $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' ) @@ -198,30 +210,33 @@ public function testFetchQueryResultWithResponseCache() { ->method( 'getDescription' ) ->willReturn( $description ); + $this->jsonResponseParser->expects( $this->any() ) + ->method( 'getResultSubjectList' ) + ->willReturn( [] ); + + // The live fetch must happen exactly once across two identical queries; + // the second call has to be served from the response cache. + $this->httpRequest->expects( $this->once() ) + ->method( 'getContent' ) + ->willReturn( json_encode( [ 'query' => [] ] ) ); + $instance = new QueryResultFetcher( $this->httpRequestFactory, + $cache, $queryResultFactory, $this->jsonResponseParser, [] ); + $instance->setHttpRequestEndpoint( 'http://example.org/api.php' ); + $instance->setRepositoryTargetUrl( 'http://example.org/$1' ); $instance->setHttpResponseCacheLifetime( 42 ); $instance->setHttpResponseCachePrefix( 'Foo' ); - // PHUNIT 4.1 - // $this->httpRequest->expects( $this->any() ) - // ->method( 'setOption' ) - // ->withConsecutive( - // array( $this->anything(), $this->equalTo( 42 ) ), - // array( $this->anything(), $this->equalTo( 'Foo:' ) ) ); - - $this->httpRequest->expects( $this->at( 0 ) ) - ->method( 'setOption' ) - ->with( $this->anything(), 42 ); - - $this->httpRequest->expects( $this->at( 1 ) ) - ->method( 'setOption' ) - ->with( $this->anything(), 'Foo:seql:' ); + $this->assertInstanceOf( + '\SMW\Query\QueryResult', + $instance->fetchQueryResult( $query ) + ); $this->assertInstanceOf( '\SMW\Query\QueryResult', @@ -266,6 +281,7 @@ public function testHttpRequestToReturnWithError() { $instance = new QueryResultFetcher( $this->httpRequestFactory, + $this->cache, $queryResultFactory, $this->jsonResponseParser, [] @@ -275,7 +291,7 @@ public function testHttpRequestToReturnWithError() { $instance->setRepositoryTargetUrl( 'http://example.org/$1' ); $this->httpRequest->expects( $this->any() ) - ->method( 'execute' ) + ->method( 'getContent' ) ->willReturn( json_encode( [ 'error' => [ 'info' => 'error' ] ] ) ); $this->assertInstanceOf( @@ -325,6 +341,7 @@ public function testHttpRequestToReturnWithValidJson() { $instance = new QueryResultFetcher( $this->httpRequestFactory, + $this->cache, $queryResultFactory, $this->jsonResponseParser, [] @@ -339,7 +356,7 @@ public function testHttpRequestToReturnWithValidJson() { ]; $this->httpRequest->expects( $this->once() ) - ->method( 'execute' ) + ->method( 'getContent' ) ->willReturn( json_encode( $expected ) ); $this->assertInstanceOf( From a338773579743ad0e5d3f7241b0873f5092437b6 Mon Sep 17 00:00:00 2001 From: alistair3149 Date: Fri, 5 Jun 2026 17:43:51 -0400 Subject: [PATCH 3/6] Add security policy Document how to privately report security vulnerabilities via GitHub's private vulnerability reporting, the disclosure process, and where to report issues that belong to MediaWiki or Semantic MediaWiki instead. --- SECURITY.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..4f560ba --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,51 @@ +# Security Policy + +## Supported versions + +Security fixes are provided for the latest major version of Semantic External +Query Lookup. Please upgrade to the current release before reporting an issue. +Fixes ship in a new release rather than as backports to older versions. + +## Reporting a vulnerability + +Please do **not** report security vulnerabilities through public GitHub issues, +pull requests, the mailing list, or the project wiki. + +Instead, report them privately using GitHub's +[private vulnerability reporting](https://github.com/SemanticMediaWiki/SemanticExternalQueryLookup/security/advisories/new). +Please include the affected version, steps to reproduce, and the potential +impact. + +A maintainer will respond to your report, keep you informed of the progress +towards a fix, and may ask for additional information. + +## Disclosure process + +To minimise the risk of exploitation, please give us a reasonable opportunity to +release a fix before any public disclosure. After a report is submitted, we aim +to: + +- Acknowledge the report within 15 days. +- Confirm the issue and assess its severity and impact. +- Prepare and release a fix, prioritised by severity, keeping the reporter + informed of progress. +- Publish a security advisory once a fix is available, crediting the reporter + unless they prefer to remain anonymous. + +Remediation time depends on the severity and complexity of the issue. For +coordinated disclosure we aim to release a fix within 90 days where feasible. + +Because the repository is public and can be watched by potential attackers, +please avoid describing the vulnerability in public channels, including commit +messages and issue comments, until a fix has been released. + +## Vulnerabilities in MediaWiki or Semantic MediaWiki + +Semantic External Query Lookup is an extension to MediaWiki that builds on +Semantic MediaWiki. If the issue is actually in one of those rather than in +Semantic External Query Lookup itself, please report it there instead: + +- For Semantic MediaWiki, use its + [private vulnerability reporting](https://github.com/SemanticMediaWiki/SemanticMediaWiki/security/advisories/new). +- For MediaWiki core or another extension, contact the + [Wikimedia security team](https://www.mediawiki.org/wiki/Reporting_security_bugs). From 74922798e9fa3df199a79e151b067a41f11b8024 Mon Sep 17 00:00:00 2001 From: alistair3149 Date: Fri, 5 Jun 2026 18:04:10 -0400 Subject: [PATCH 4/6] Use the namespaced MediaWiki\Title\Title The global `Title` alias is not reliably autoloadable on its own under MediaWiki 1.43+ (it only exists once MediaWiki\Title\Title has been loaded). Depending on the PHPUnit execution order this surfaced as `Class "Title" not found` errors in the unit tests on PHP 8.2/8.3/8.4 in CI, while passing on PHP 8.1. Reference the canonical MediaWiki\Title\Title directly so the class is always autoloaded regardless of order. --- src/DataValueDeserializer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/DataValueDeserializer.php b/src/DataValueDeserializer.php index 8e56266..3ed508c 100644 --- a/src/DataValueDeserializer.php +++ b/src/DataValueDeserializer.php @@ -2,6 +2,7 @@ namespace SEQL; +use MediaWiki\Title\Title; use SMW\DataItems\Blob; use SMW\DataItems\Container; use SMW\DataItems\Property; @@ -97,7 +98,7 @@ public function newDiWikiPage( array $value ) { $value['fulltext'] = substr( $value['fulltext'], ( $pos = strpos( $value['fulltext'], ':' ) ) !== false ? $pos + 1 : 0 ); } - $title = \Title::newFromText( $this->querySource . ':' . str_replace( " ", "_", $value['fulltext'] ), $ns ); + $title = Title::newFromText( $this->querySource . ':' . str_replace( " ", "_", $value['fulltext'] ), $ns ); return WikiPage::newFromTitle( $title ); } From 71797efc50deb6b6f2d5c8df12149e03b755d1a4 Mon Sep 17 00:00:00 2001 From: alistair3149 Date: Fri, 5 Jun 2026 18:15:31 -0400 Subject: [PATCH 5/6] Fix invalid @covers annotations in tests Both integration tests carried `@covers I18nJsonFileIntegrity`, which is not a valid class or method reference. PHPUnit's strict coverage validation reports it as an invalid annotation, and the resulting warnings (one per data-provider row) fail CI on PHP 8.2+. Point the faux-response test at the class it actually exercises (`\SEQL\ByHttpRequest\JsonResponseParser`) and mark the i18n file integrity test, which covers no source class, as `@coversNothing`. --- .../ByHttpRequest/JsonParseOnFauxHttpResponseTest.php | 2 +- tests/phpunit/Integration/I18nJsonFileIntegrityTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/Integration/ByHttpRequest/JsonParseOnFauxHttpResponseTest.php b/tests/phpunit/Integration/ByHttpRequest/JsonParseOnFauxHttpResponseTest.php index b62128e..457780a 100644 --- a/tests/phpunit/Integration/ByHttpRequest/JsonParseOnFauxHttpResponseTest.php +++ b/tests/phpunit/Integration/ByHttpRequest/JsonParseOnFauxHttpResponseTest.php @@ -37,7 +37,7 @@ protected function setUp(): void { } /** - * @covers I18nJsonFileIntegrity + * @covers \SEQL\ByHttpRequest\JsonResponseParser * @dataProvider jsonFileProvider */ public function testQueryResultFetcherFromCannedJsonResponse( $file ) { diff --git a/tests/phpunit/Integration/I18nJsonFileIntegrityTest.php b/tests/phpunit/Integration/I18nJsonFileIntegrityTest.php index 2df348b..ee1de03 100644 --- a/tests/phpunit/Integration/I18nJsonFileIntegrityTest.php +++ b/tests/phpunit/Integration/I18nJsonFileIntegrityTest.php @@ -16,7 +16,7 @@ class I18nJsonFileIntegrityTest extends \PHPUnit\Framework\TestCase { /** - * @covers I18nJsonFileIntegrity + * @coversNothing * @dataProvider i18nFileProvider */ public function testI18NJsonDecodeEncode( $file ) { From 07a25331c5c075c25d1a567a2e80c209a99dd30b Mon Sep 17 00:00:00 2001 From: alistair3149 Date: Fri, 5 Jun 2026 18:22:00 -0400 Subject: [PATCH 6/6] Update mediawiki-codesniffer to 48.0.0 for PHP 8.4 Version 43.0.0 bundles PHP_CodeSniffer / PHPCSUtils releases that emit an "implicitly nullable parameter" deprecation under PHP 8.4, which fails the 8.4 CI job during the phpcs step. Bump to 48.0.0 (the version Semantic MediaWiki core uses), pulling PHP_CodeSniffer 3.13 and PHPCSUtils 1.1, which are PHP 8.4 clean. Also declare the ValidGlobalName allowedPrefixes property in .phpcs.xml with nodes, replacing the comma-separated string form that is deprecated in PHP_CodeSniffer 3.3. --- .phpcs.xml | 5 ++++- composer.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.phpcs.xml b/.phpcs.xml index 866e637..4f0dfe6 100644 --- a/.phpcs.xml +++ b/.phpcs.xml @@ -30,7 +30,10 @@ - + + + + diff --git a/composer.json b/composer.json index 979e54e..40b0a8e 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ "composer/installers": ">=1.0.1" }, "require-dev": { - "mediawiki/mediawiki-codesniffer": "43.0.0", + "mediawiki/mediawiki-codesniffer": "48.0.0", "mediawiki/minus-x": "1.1.3", "php-parallel-lint/php-console-highlighter": "1.0.0", "php-parallel-lint/php-parallel-lint": "1.4.0"