diff --git a/.circleci/config.yml b/.circleci/config.yml index 231f88af..6519b6c8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -57,6 +57,8 @@ commands: path: test-results/junit.xml - store_artifacts: path: test-results/junit.xml + - store_artifacts: + path: profiler jobs: lint: @@ -66,6 +68,7 @@ jobs: - checkout - run: composer install - run: composer lint + - run: composer analyse test: parameters: @@ -149,10 +152,6 @@ workflows: build_and_test: jobs: - lint - - test: - name: test-php73 - php_version: php73 - mongo_version: 3.6.23 - test: name: test-php74 php_version: php74 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..cb2cfb7c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +/.circleci/ export-ignore +/docker export-ignore +/test export-ignore +.editorconfig export-ignore +.env export-ignore +.php-cs-fixer* export-ignore +*.log export-ignore +docker-compose*.yml export-ignore +logo.png export-ignore +phpunit.xml export-ignore +phpstan*.neon export-ignore diff --git a/.gitignore b/.gitignore index dfa7f653..ad315119 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ tags atlassian-ide-plugin.xml composer.lock +profiler test-results .phpunit.result.cache .php-cs-fixer.cache diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index c9c91693..822972a2 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -10,10 +10,25 @@ ->setRules([ '@auto' => true, '@PhpCsFixer' => true, + 'blank_line_before_statement' => ['statements' => [ + 'break', + 'case', + 'continue', + 'declare', + 'default', + 'exit', + 'goto', + 'phpdoc', + 'return', + 'switch', + 'throw', + 'try', + ]], 'concat_space' => ['spacing' => 'one'], 'increment_style' => false, 'multiline_whitespace_before_semicolons' => ['strategy' => 'no_multi_line'], 'ordered_types' => ['null_adjustment' => 'always_last'], + 'phpdoc_no_empty_return' => false, 'phpdoc_types_order' => ['null_adjustment' => 'always_last'], 'yoda_style' => false, 'php_unit_method_casing' => false, @@ -23,5 +38,7 @@ ->setFinder( (new Finder()) ->in(__DIR__) + ->ignoreVCSIgnored(true) ->ignoreDotFiles(false) + ->notPath('rector.php') ); diff --git a/composer.json b/composer.json index cdc4437c..c6a53d78 100644 --- a/composer.json +++ b/composer.json @@ -17,16 +17,11 @@ "homepage": "http://talis.com/" } ], - "config": { - "audit": { - "ignore": ["PKSA-gk21-j82g-2hhy"] - } - }, "suggest": { "resque/php-resque": "Redis backed library for background jobs" }, "require": { - "php" : ">=7.3", + "php" : ">=7.4", "mongodb/mongodb": "*", "monolog/monolog" : "~1.13", "semsol/arc2": "2.2.6" @@ -34,7 +29,9 @@ "require-dev": { "phpunit/phpunit": "^9.6.20", "resque/php-resque": "v1.3.6", - "friendsofphp/php-cs-fixer": "^3.4" + "friendsofphp/php-cs-fixer": "^3.4", + "perftools/php-profiler": "^1.2", + "phpstan/phpstan": "^2.1" }, "autoload": { "classmap": ["src/"] @@ -44,6 +41,7 @@ }, "scripts": { "lint": "php-cs-fixer check", + "analyse": "phpstan analyse --memory-limit 1024M", "test": [ "Composer\\Config::disableProcessTimeout", "phpunit" diff --git a/docker-compose.yml b/docker-compose.yml index 9567b632..81a577f0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,13 +8,6 @@ x-base-config: &base-config env_file: .env services: - php73: - build: - context: ./docker - dockerfile: Dockerfile-php73 - image: talis/tripod-php:php73-latest - <<: *base-config - php74: build: context: ./docker diff --git a/docker/Dockerfile-php73 b/docker/Dockerfile-php73 deleted file mode 100644 index 56b1bced..00000000 --- a/docker/Dockerfile-php73 +++ /dev/null @@ -1,18 +0,0 @@ -FROM php:7.3.33-cli - -RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates \ - curl \ - git \ - unzip \ - zip \ - && rm -rf /var/lib/apt/lists/* - -RUN curl -sLo /tmp/mongosh.deb https://downloads.mongodb.com/compass/mongodb-mongosh_2.2.15_amd64.deb \ - && dpkg -i /tmp/mongosh.deb \ - && rm /tmp/mongosh.deb - -COPY --from=mlocati/php-extension-installer:2.3.2 /usr/bin/install-php-extensions /usr/local/bin/ -COPY --from=composer:2.7.7 /usr/bin/composer /usr/local/bin/ - -RUN install-php-extensions pcntl mongodb-1.6.1 diff --git a/docker/Dockerfile-php74 b/docker/Dockerfile-php74 index 45947dac..86d86b60 100644 --- a/docker/Dockerfile-php74 +++ b/docker/Dockerfile-php74 @@ -15,4 +15,4 @@ RUN curl -sLo /tmp/mongosh.deb https://downloads.mongodb.com/compass/mongodb-mon COPY --from=mlocati/php-extension-installer:2.3.2 /usr/bin/install-php-extensions /usr/local/bin/ COPY --from=composer:2.7.7 /usr/bin/composer /usr/local/bin/ -RUN install-php-extensions pcntl mongodb-1.19.3 +RUN install-php-extensions pcntl mongodb-1.19.3 xhprof diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 00000000..661d7431 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,17 @@ +parameters: + level: 5 + phpVersion: + min: 70400 # PHP 7.4 + max: 80419 # PHP 8.4.19 + tips: + treatPhpDocTypesAsCertain: false + ignoreErrors: + - '#Access to undefined constant MongoDB\\Driver\\ReadPreference\:\:RP_.*#' + - '#Call to an undefined method MongoDB\\Driver\\ReadPreference\:\:getMode\(\)#' + - identifier: missingType.iterableValue + reportUnmatched: false + - identifier: unset.possiblyHookedProperty + reportUnmatched: false + paths: + - src + - test diff --git a/phpunit.xml b/phpunit.xml index 83a2fac8..e5c703ab 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -5,11 +5,11 @@ - - test/performance - test/unit + + test/performance + diff --git a/scripts/mongo/BSONToQuads.php b/scripts/mongo/BSONToQuads.php index a95f2900..15eda59c 100644 --- a/scripts/mongo/BSONToQuads.php +++ b/scripts/mongo/BSONToQuads.php @@ -5,10 +5,9 @@ use Tripod\Mongo\TriplesUtil; require_once __DIR__ . '/common.inc.php'; - require_once dirname(__FILE__, 3) . '/src/tripod.inc.php'; -if ($argc != 2) { +if ($argc !== 2) { echo "usage: ./BSONToQuads.php tripodConfig.json < bsondata\n"; echo " When exporting bson data from Mongo use: \n"; echo " mongoexport -d -c > bsondata.txt \n"; diff --git a/scripts/mongo/BSONToTriples.php b/scripts/mongo/BSONToTriples.php index a6698828..26ebf821 100644 --- a/scripts/mongo/BSONToTriples.php +++ b/scripts/mongo/BSONToTriples.php @@ -5,16 +5,16 @@ use Tripod\Mongo\TriplesUtil; require_once __DIR__ . '/common.inc.php'; - require_once dirname(__FILE__, 3) . '/src/tripod.inc.php'; -if ($argc != 2) { +if ($argc !== 2) { echo "usage: ./BSONToTriples.php tripodConfig.json < bsondata\n"; echo " When exporting bson data from Mongo use: \n"; echo " mongoexport -d -c > bsondata.txt \n"; exit; } + array_shift($argv); $config = json_decode(file_get_contents($argv[0]), true); Config::setConfig($config); diff --git a/scripts/mongo/common.inc.php b/scripts/mongo/common.inc.php index 3b492815..0d4cd5e7 100644 --- a/scripts/mongo/common.inc.php +++ b/scripts/mongo/common.inc.php @@ -13,11 +13,9 @@ } if (!defined('TRIPOD_COMPOSER_INSTALL')) { - exit( - 'You need to set up the project dependencies using the following commands:' . PHP_EOL + exit('You need to set up the project dependencies using the following commands:' . PHP_EOL . 'curl -sS https://getcomposer.org/installer | php' . PHP_EOL - . 'php composer.phar install' . PHP_EOL - ); + . 'php composer.phar install' . PHP_EOL); } require TRIPOD_COMPOSER_INSTALL; diff --git a/scripts/mongo/createSearchDocuments.php b/scripts/mongo/createSearchDocuments.php index b40d498d..f6b72b41 100644 --- a/scripts/mongo/createSearchDocuments.php +++ b/scripts/mongo/createSearchDocuments.php @@ -1,7 +1,7 @@ getSearchDocumentSpecification($storeName, $specId); if (array_key_exists('from', $spec)) { Config::getInstance()->setMongoCursorTimeout(-1); - echo "Generating {$specId}"; + echo 'Generating ' . $specId; $tripod = new Driver($spec['from'], $storeName, ['stat' => $stat]); $search = $tripod->getSearchIndexer(); if ($id) { @@ -85,31 +83,13 @@ function generateSearchDocuments($id, $specId, $storeName, $stat = null, $queue Config::setConfig(json_decode(file_get_contents($configLocation), true)); -if (isset($options['s']) || isset($options['storename'])) { - $storeName = $options['s'] ?? $options['storename']; -} else { - $storeName = null; -} - -if (isset($options['d']) || isset($options['spec'])) { - $specId = isset($options['d']) ? $options['t'] : $options['spec']; -} else { - $specId = null; -} - -if (isset($options['i']) || isset($options['id'])) { - $id = $options['i'] ?? $options['id']; -} else { - $id = null; -} +$storeName = $options['s'] ?? $options['storename'] ?? null; +$specId = $options['d'] ?? $options['spec'] ?? null; +$id = $options['i'] ?? $options['id'] ?? null; $queue = null; if (isset($options['a']) || isset($options['async'])) { - if (isset($options['q']) || isset($options['queue'])) { - $queue = $options['queue']; - } else { - $queue = Config::getInstance()->getApplyQueueName(); - } + $queue = $options['q'] ?? $options['queue'] ?? Config::getInstance()->getApplyQueueName(); } $stat = null; diff --git a/scripts/mongo/createTables.php b/scripts/mongo/createTables.php index 10f70370..3fb72b69 100644 --- a/scripts/mongo/createTables.php +++ b/scripts/mongo/createTables.php @@ -20,14 +20,15 @@ ] ); -function showUsage() +function showUsage(): void { - $help = <<<'END' - createTables.php + $scriptName = basename(__FILE__); + $help = <<getTableSpecification($storeName, $tableId); if (array_key_exists('from', $tableSpec)) { Config::getInstance()->setMongoCursorTimeout(-1); - echo "Generating {$tableId}"; + echo 'Generating ' . $tableId; $tripod = new Driver($tableSpec['from'], $storeName, ['stat' => $stat]); $tTables = $tripod->getTripodTables(); if ($id) { @@ -87,31 +87,13 @@ function generateTables($id, $tableId, $storeName, $stat = null, $queue = null) Config::setConfig(json_decode(file_get_contents($configLocation), true)); -if (isset($options['s']) || isset($options['storename'])) { - $storeName = $options['s'] ?? $options['storename']; -} else { - $storeName = null; -} - -if (isset($options['t']) || isset($options['spec'])) { - $tableId = $options['t'] ?? $options['spec']; -} else { - $tableId = null; -} - -if (isset($options['i']) || isset($options['id'])) { - $id = $options['i'] ?? $options['id']; -} else { - $id = null; -} +$storeName = $options['s'] ?? $options['storename'] ?? null; +$tableId = $options['t'] ?? $options['spec'] ?? null; +$id = $options['i'] ?? $options['id'] ?? null; $queue = null; if (isset($options['a']) || isset($options['async'])) { - if (isset($options['q']) || isset($options['queue'])) { - $queue = $options['queue']; - } else { - $queue = Config::getInstance()->getApplyQueueName(); - } + $queue = $options['q'] ?? $options['queue'] ?? Config::getInstance()->getApplyQueueName(); } $stat = null; diff --git a/scripts/mongo/createViews.php b/scripts/mongo/createViews.php index 3537f2f2..2e27b2ed 100644 --- a/scripts/mongo/createViews.php +++ b/scripts/mongo/createViews.php @@ -20,14 +20,15 @@ ] ); -function showUsage() +function showUsage(): void { - $help = <<<'END' - createViews.php + $scriptName = basename(__FILE__); + $help = <<getViewSpecification($storeName, $viewId); if (array_key_exists('from', $viewSpec)) { Config::getInstance()->setMongoCursorTimeout(-1); - echo "Generating {$viewId}"; + echo 'Generating ' . $viewId; $tripod = new Driver($viewSpec['from'], $storeName, ['stat' => $stat]); $views = $tripod->getTripodViews(); if ($id) { @@ -87,31 +86,13 @@ function generateViews($id, $viewId, $storeName, $stat, $queue) Config::setConfig(json_decode(file_get_contents($configLocation), true)); -if (isset($options['s']) || isset($options['storename'])) { - $storeName = $options['s'] ?? $options['storename']; -} else { - $storeName = null; -} - -if (isset($options['v']) || isset($options['spec'])) { - $viewId = $options['v'] ?? $options['spec']; -} else { - $viewId = null; -} - -if (isset($options['i']) || isset($options['id'])) { - $id = $options['i'] ?? $options['id']; -} else { - $id = null; -} +$storeName = $options['s'] ?? $options['storename'] ?? null; +$viewId = $options['v'] ?? $options['spec'] ?? null; +$id = $options['i'] ?? $options['id'] ?? null; $queue = null; if (isset($options['a']) || isset($options['async'])) { - if (isset($options['q']) || isset($options['queue'])) { - $queue = $options['queue']; - } else { - $queue = Config::getInstance()->getApplyQueueName(); - } + $queue = $options['q'] ?? $options['queue'] ?? Config::getInstance()->getApplyQueueName(); } $stat = null; diff --git a/scripts/mongo/detectNamespaces.php b/scripts/mongo/detectNamespaces.php index ad3c9b93..dc3f6a5a 100644 --- a/scripts/mongo/detectNamespaces.php +++ b/scripts/mongo/detectNamespaces.php @@ -4,16 +4,16 @@ use Tripod\Mongo\TriplesUtil; require_once __DIR__ . '/common.inc.php'; - require_once dirname(__FILE__, 3) . '/src/tripod.inc.php'; ini_set('memory_limit', '32M'); -if ($argc != 1) { +if ($argc !== 1) { echo "usage: php detectNamespaces.php < triples\n"; exit; } + array_shift($argv); $dummyDbConfig = [ @@ -51,16 +51,18 @@ $parts = preg_split('/\s/', $line); $subject = trim($parts[0], '><'); - if (($i % 2500) == 0) { + if ($i % 2500 === 0) { echo '.'; } - if (($i % 50000) == 0) { + + if ($i % 50000 === 0) { foreach ($objectNs as $key => $val) { if ($val < 5) { // flush unset($objectNs[$key]); } } + gc_collect_cycles(); echo 'F'; } @@ -69,21 +71,23 @@ $currentSubject = $subject; } elseif ($currentSubject != $subject) { // once subject changes, we have all triples for that subject, flush to Mongo $ns = $util->extractMissingPredicateNs($triples); - if (count($ns) > 0) { + if ($ns !== []) { $newNsConfig = []; foreach ($ns as $n) { $prefix = $util->suggestPrefix($n); if (array_key_exists($prefix, $config['namespaces'])) { $prefix .= uniqid(); } + $newNsConfig[$prefix] = $n; echo "\nFound ns {$n} suggest prefix {$prefix}"; $config['namespaces'] = array_merge($config['namespaces'], $newNsConfig); Config::setConfig($config); } } + $ns = $util->extractMissingObjectNs($triples); - if (count($ns) > 0) { + if ($ns !== []) { $newNsConfig = []; foreach ($ns as $n) { if (array_key_exists($n, $objectNs)) { @@ -91,11 +95,13 @@ } else { $objectNs[$n] = 1; } + if ($objectNs[$n] > 500) { $prefix = $util->suggestPrefix($n); if (array_key_exists($prefix, $config['namespaces'])) { $prefix .= uniqid(); } + $newNsConfig[$prefix] = $n; echo "\nFound object ns {$n} occurs > 500 times, suggest prefix {$prefix}"; $config['namespaces'] = array_merge($config['namespaces'], $newNsConfig); @@ -107,66 +113,9 @@ $currentSubject = $subject; // reset current subject to next subject $triples = []; // reset triples } + $triples[] = $line; } echo "Suggested namespace configuration:\n\n"; - -/** - * @param string $json - * - * @return string - */ -function indent($json) -{ - $result = ''; - $pos = 0; - $strLen = strlen($json); - $indentStr = ' '; - $newLine = "\n"; - $prevChar = ''; - $outOfQuotes = true; - - for ($i = 0; $i <= $strLen; $i++) { - // Grab the next character in the string. - $char = substr($json, $i, 1); - - // Are we inside a quoted string? - if ($char == '"' && $prevChar != '\\') { - $outOfQuotes = !$outOfQuotes; - - // If this character is the end of an element, - // output a new line and indent the next line. - } elseif (($char == '}' || $char == ']') && $outOfQuotes) { - $result .= $newLine; - $pos--; - for ($j = 0; $j < $pos; $j++) { - $result .= $indentStr; - } - } - - // Add the character to the result string. - $result .= $char; - - // If the last character was the beginning of an element, - // output a new line and indent the next line. - if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { - $result .= $newLine; - if ($char == '{' || $char == '[') { - $pos++; - } - - for ($j = 0; $j < $pos; $j++) { - $result .= $indentStr; - } - } - - $prevChar = $char; - } - - return $result; -} - -$json = json_encode(['namespaces' => $config['namespaces']]); - -echo indent($json); +echo json_encode(['namespaces' => $config['namespaces']], JSON_PRETTY_PRINT); diff --git a/scripts/mongo/discoverUnnamespacedUris.php b/scripts/mongo/discoverUnnamespacedUris.php index 86c71458..48e92dda 100644 --- a/scripts/mongo/discoverUnnamespacedUris.php +++ b/scripts/mongo/discoverUnnamespacedUris.php @@ -6,7 +6,7 @@ require_once __DIR__ . '/common.inc.php'; // Detects un-namespaced subjects or object uris in CBD collections of the target database. Optionally supply a base uri to match against that rather than all uris -if ($argc != 4 && $argc != 3) { +if ($argc !== 4 && $argc !== 3) { echo 'usage: php discoverUnnamespacedUris.php connStr database [baseUri]'; exit; @@ -27,10 +27,8 @@ /** * @param string $uri * @param string|null $baseUri - * - * @return bool */ -function isUnNamespaced($uri, $baseUri = null) +function isUnNamespaced($uri, $baseUri = null): bool { if ($baseUri == null) { return strpos($uri, 'http://') === 0 || strpos($uri, 'https://') === 0; @@ -44,17 +42,16 @@ function isUnNamespaced($uri, $baseUri = null) // @var \MongoDB\Collection $collection if (strpos($collectionInfo->getName(), 'CBD_') === 0) { // only process CBD_collections $collection = $db->selectCollection($collectionInfo->getName()); - echo "Checking out {$collectionInfo->getName()}\n"; + echo sprintf('Checking out %s%s', $collectionInfo->getName(), PHP_EOL); $count = 0; foreach ($collection->find() as $doc) { if (!isset($doc['_id']) || !isset($doc['_id']['r'])) { echo ' Illegal doc: no _id or missing _id.r'; - } else { - if (isUnNamespaced($doc['_id']['r'], $argv[2] ?? null)) { - echo " Un-namespaced subject: {$doc['_id']['r']}\n"; - $count++; - } + } elseif (isUnNamespaced($doc['_id']['r'], $argv[2] ?? null)) { + echo sprintf(' Un-namespaced subject: %s%s', $doc['_id']['r'], PHP_EOL); + $count++; } + foreach ($doc as $property => $value) { if (strpos($property, '_') === 0) { // ignore meta fields, _id, _version, _uts etc. continue; @@ -64,25 +61,32 @@ function isUnNamespaced($uri, $baseUri = null) // ignore, is a literal continue; } + if (isset($value['u'])) { if (isUnNamespaced($value['u'], $argv[2] ?? null)) { - echo " Un-namespaced object uri (single value): {$value['u']}\n"; + echo sprintf(' Un-namespaced object uri (single value): %s%s', $value['u'], PHP_EOL); $count++; } } else { foreach ($value as $v) { - if (isset($v['u'])) { - if (isUnNamespaced($v['u'], $argv[2] ?? null)) { - echo " Un-namespaced object uri (multiple value): {$v['u']}\n"; - $count++; - } + if (!isset($v['u'])) { + continue; + } + + if (!isUnNamespaced($v['u'], $argv[2] ?? null)) { + continue; } + + echo sprintf(' Un-namespaced object uri (multiple value): %s%s', $v['u'], PHP_EOL); + $count++; } } } } - $results[] = "{$collectionInfo->getName()} has {$count} un-namespaced uris"; - echo "Done with {$collectionInfo->getName()}\n"; + + $results[] = sprintf('%s has %d un-namespaced uris', $collectionInfo->getName(), $count); + echo sprintf('Done with %s%s', $collectionInfo->getName(), PHP_EOL); } } + echo "\n" . implode("\n", $results) . "\n"; diff --git a/scripts/mongo/ensureIndexes.php b/scripts/mongo/ensureIndexes.php index 2f9548a5..c6c23599 100644 --- a/scripts/mongo/ensureIndexes.php +++ b/scripts/mongo/ensureIndexes.php @@ -5,20 +5,20 @@ use Tripod\Timer; require_once __DIR__ . '/common.inc.php'; - require_once dirname(__FILE__, 3) . '/src/tripod.inc.php'; -if ($argc != 2 && $argc != 3 && $argc != 4) { +if (!in_array($argc, [2, 3, 4])) { echo "usage: php ensureIndexes.php tripodConfig.json [storeName] [forceReindex (default is false)] [background (default is true)]\n"; exit; } + array_shift($argv); Config::setConfig(json_decode(file_get_contents($argv[0]), true)); $storeName = $argv[1] ?? null; -$forceReindex = (isset($argv[2]) && ($argv[2] == 'true')) ? true : false; +$forceReindex = isset($argv[2]) && ($argv[2] == 'true'); $background = (isset($argv[3]) && ($argv[3] == 'false')) ? false : true; Config::getInstance()->setMongoCursorTimeout(-1); diff --git a/scripts/mongo/loadTriples.php b/scripts/mongo/loadTriples.php index 23563012..4349def3 100644 --- a/scripts/mongo/loadTriples.php +++ b/scripts/mongo/loadTriples.php @@ -5,20 +5,17 @@ use Tripod\Timer; require_once __DIR__ . '/common.inc.php'; - require_once dirname(__FILE__, 3) . '/src/tripod.inc.php'; /** - * @param string $subject * @param string $podName - * @param string $storeName */ -function load(TriplesUtil $loader, $subject, array $triples, array &$errors, $podName, $storeName) +function load(TriplesUtil $loader, string $subject, array $triples, array &$errors, $podName, string $storeName): void { try { $loader->loadTriplesAbout($subject, $triples, $storeName, $podName); } catch (Exception $e) { - echo "Exception for subject {$subject} failed with message: " . $e->getMessage() . "\n"; + echo sprintf('Exception for subject %s failed with message: ', $subject) . $e->getMessage() . "\n"; $errors[] = $subject; } } @@ -26,11 +23,12 @@ function load(TriplesUtil $loader, $subject, array $triples, array &$errors, $po $timer = new Timer(); $timer->start(); -if ($argc != 4) { +if ($argc !== 4) { echo "usage: ./loadTriples.php storename podname tripodConfig.json < ntriplesdata\n"; exit; } + array_shift($argv); $storeName = $argv[0]; @@ -46,7 +44,7 @@ function load(TriplesUtil $loader, $subject, array $triples, array &$errors, $po while (($line = fgets(STDIN)) !== false) { $i++; - if (($i % 250000) == 0) { + if ($i % 250000 === 0) { echo 'Memory: ' . memory_get_usage() . "\n"; } @@ -54,13 +52,14 @@ function load(TriplesUtil $loader, $subject, array $triples, array &$errors, $po $parts = preg_split('/\s/', $line); $subject = trim($parts[0], '><'); - if (empty($currentSubject)) { // set for first iteration + if ($currentSubject === '') { // set for first iteration $currentSubject = $subject; - } elseif ($currentSubject != $subject) { // once subject changes, we have all triples for that subject, flush to Mongo + } elseif ($currentSubject !== $subject) { // once subject changes, we have all triples for that subject, flush to Mongo load($loader, $currentSubject, $triples, $errors, $podName, $storeName); $currentSubject = $subject; // reset current subject to next subject $triples = []; // reset triples } + $triples[] = $line; } @@ -71,7 +70,7 @@ function load(TriplesUtil $loader, $subject, array $triples, array &$errors, $po echo 'This script ran in ' . $timer->result() . " milliseconds\n"; echo 'Processed ' . $i . ' triples'; -if (count($errors) > 0) { +if ($errors !== []) { echo 'Insert errors on ' . count($errors) . " subjects\n"; var_dump($errors); // todo: decide what to do with errors... } diff --git a/scripts/mongo/triplesToBSON.php b/scripts/mongo/triplesToBSON.php index 9c71d554..330aa3ff 100644 --- a/scripts/mongo/triplesToBSON.php +++ b/scripts/mongo/triplesToBSON.php @@ -4,14 +4,14 @@ use Tripod\Mongo\TriplesUtil; require_once __DIR__ . '/common.inc.php'; - require_once dirname(__FILE__, 3) . '/src/tripod.inc.php'; -if ($argc != 2) { +if ($argc !== 2) { echo "usage: ./triplesToBSON.php tripodconfig.json < ntriplesdata\n"; exit; } + array_shift($argv); $config = json_decode(file_get_contents($argv[0]), true); @@ -29,11 +29,11 @@ $parts = preg_split('/\s/', $line); $subject = trim($parts[0], '><'); - if (empty($currentSubject)) { // set for first iteration + if ($currentSubject === '') { // set for first iteration $currentSubject = $subject; } - if ($currentSubject != $subject) { // once subject changes, we have all triples for that subject, flush to Mongo + if ($currentSubject !== $subject) { // once subject changes, we have all triples for that subject, flush to Mongo echo json_encode($tu->getTArrayAbout($currentSubject, $triples, Config::getInstance()->getDefaultContextAlias())) . "\n"; $currentSubject = $subject; // reset current subject to next subject $triples = []; // reset triples @@ -44,6 +44,3 @@ // last doc echo json_encode($tu->getTArrayAbout($currentSubject, $triples, Config::getInstance()->getDefaultContextAlias())) . "\n"; - -?> - diff --git a/scripts/mongo/validateConfig.php b/scripts/mongo/validateConfig.php index d378b1c6..d01d746b 100644 --- a/scripts/mongo/validateConfig.php +++ b/scripts/mongo/validateConfig.php @@ -12,27 +12,33 @@ ] ); -function showUsage() +function showUsage(): void { - $help = <<<'END' - validateConfig.php + $scriptName = basename(__FILE__); + $help = <<pushHandler(new StreamHandler('php://stderr', LogLevel::WARNING)); // resque too chatty on NOTICE & INFO. YMMV diff --git a/src/Config.php b/src/Config.php index ce54a47b..6f2c5f86 100644 --- a/src/Config.php +++ b/src/Config.php @@ -1,5 +1,7 @@ $fields array of fields, in the same format as prescribed by MongoPHP */ - public function select(array $query, array $fields, ?array $sortBy = null, $limit = null, $offset = 0, $context = null); + public function select( + array $query, + array $fields, + ?array $sortBy = null, + ?int $limit = null, + ?int $offset = 0, + ?string $context = null + ): array; /** * Select data from a table. - * - * @param string $tableType - * @param int $offset - * @param int $limit - * - * @return array */ public function getTableRows( - $tableType, + string $tableType, array $filter = [], - array $sortBy = [], - $offset = 0, - $limit = 10, + ?array $sortBy = [], + ?int $offset = 0, + ?int $limit = 10, array $options = [] - ); + ): array; - /** - * @param string $tableType - * @param string $fieldName - * - * @return array - */ - public function getDistinctTableColumnValues($tableType, $fieldName, array $filter = []); + public function getDistinctTableColumnValues(string $tableType, string $fieldName, array $filter = []): array; /** * Get a count of resources matching the pattern in $query. Optionally group counts by specifying a $groupBy predicate. * - * @param string|null $groupBy - * @param int|null $ttl acceptable time to live if you're willing to accept a cached version of this request + * @param int|null $ttl acceptable time to live if you're willing to accept a cached version of this request * * @return array|int multidimensional array with int values if grouped by, otherwise int */ - public function getCount(array $query, $groupBy = null, $ttl = null); + public function getCount(array $query, ?string $groupBy = null, ?int $ttl = null); /** * Save the changes between $oldGraph -> $newGraph. * - * @param string|null $context - * @param string|null $description - * * @return bool true or throws exception on error */ - public function saveChanges(ExtendedGraph $oldGraph, ExtendedGraph $newGraph, $context = null, $description = null); + public function saveChanges(ExtendedGraph $oldGraph, ExtendedGraph $newGraph, ?string $context = null, ?string $description = null): bool; /** * Register an event hook, which will be executed when the event fires. - * - * @param string $eventType */ - public function registerHook($eventType, IEventHook $hook); + public function registerHook(string $eventType, IEventHook $hook): void; // START Deprecated methods that will be removed in 1.x.x @@ -150,21 +119,15 @@ public function registerHook($eventType, IEventHook $hook); * @deprecated Use graph() instead * * @param array $filter conditions to filter by - * - * @return ExtendedGraph */ - public function describe($filter); + public function describe(array $filter): ExtendedGraph; /** * Generates table rows. * * @deprecated calling save will generate table rows - this method seems to be only used in tests and does not belong on the interface - * - * @param string $tableType - * @param string|null $resource - * @param string|null $context */ - public function generateTableRows($tableType, $resource = null, $context = null); + public function generateTableRows(string $tableType, ?string $resource = null, ?string $context = null): void; /** * Submits search params to configured search provider @@ -186,7 +149,7 @@ public function generateTableRows($tableType, $resource = null, $context = null) * @throws Exception - if search provider cannot be found * @throws SearchException - if something goes wrong */ - public function search(array $params); + public function search(array $params): array; /** * Get any documents that were left in a locked state. @@ -198,19 +161,16 @@ public function search(array $params); * * @return array of locked documents */ - public function getLockedDocuments($fromDateTime = null, $tillDateTime = null); + public function getLockedDocuments(?string $fromDateTime = null, ?string $tillDateTime = null): array; /** * Remove any inert locks left by a given transaction. * * @deprecated this is a feature of the mongo implementation - this method will move from the interface to the mongo-specific Driver class soon * - * @param string $transaction_id - * @param string $reason - * * @return bool true or throws exception on error */ - public function removeInertLocks($transaction_id, $reason); + public function removeInertLocks(string $transaction_id, string $reason): bool; // END Deprecated methods that will be removed in 1.x.x } diff --git a/src/IEventHook.php b/src/IEventHook.php index d4188e10..cc88373d 100644 --- a/src/IEventHook.php +++ b/src/IEventHook.php @@ -1,5 +1,7 @@ $config The Tripod config or serialized ITripodConfigSerializer array */ - public static function create(array $config) + public static function create(array $config): IConfigInstance { if (Config::getConfig() !== $config) { Config::setConfig($config); } + if (isset($config['class']) && class_exists($config['class'])) { - $tripodConfig = call_user_func([$config['class'], 'deserialize'], $config); - } else { - $tripodConfig = Mongo\Config::deserialize($config); + return call_user_func([$config['class'], 'deserialize'], $config); } - return $tripodConfig; + return Mongo\Config::deserialize($config); } } diff --git a/src/TripodStatFactory.php b/src/TripodStatFactory.php index d713cfc5..808818e6 100644 --- a/src/TripodStatFactory.php +++ b/src/TripodStatFactory.php @@ -1,12 +1,14 @@ $config + * * @return ITripodStat */ public static function create(array $config = []) diff --git a/src/classes/ChangeSet.php b/src/classes/ChangeSet.php index cde34d0b..cac45c1c 100644 --- a/src/classes/ChangeSet.php +++ b/src/classes/ChangeSet.php @@ -1,17 +1,44 @@ , + * 'http://purl.org/dc/terms/source'?: array|string + * } + */ + public array $a; /** * Create a new changeset. This will calculate the required additions and removals based on before and after versions of a bounded description. The args parameter is an associative array that may have the following fields: @@ -38,20 +65,6 @@ class ChangeSet extends ExtendedGraph * * @param array $a args an associative array of parameters to use when constructing the changeset */ - public $a; - public $subjectIndex = []; - public $_index = []; - - protected $subjectOfChange; - protected $before_rdfxml; - protected $after_rdfxml; - protected $createdDate; - protected $creatorName; - protected $changeReason; - protected $has_changes = false; - protected $cs_resource; - protected $include_count = 0; - public function __construct(array $a) { parent::__construct(); @@ -65,9 +78,7 @@ public function __construct(array $a) $parser->parse(false, $a[$rdf]); $a[$rdf] = $parser->getSimpleIndex(0); } elseif ( - is_array($a[$rdf]) - and isset($a[$rdf][0]) - and isset($a[$rdf][0]['s']) + is_array($a[$rdf]) && isset($a[$rdf][0], $a[$rdf][0]['s']) ) { // triples array /** @var \ARC2_RDFSerializer $ser */ $ser = \ARC2::getTurtleSerializer(); @@ -80,37 +91,31 @@ public function __construct(array $a) $parser->parse(false, $turtle); $a[$rdf] = $parser->getSimpleIndex(0); } + $nrdf = str_replace('_rdfxml', '', $rdf); $this->{$nrdf} = $a[$rdf]; } } + $this->__init(); } - protected function __init() + protected function __init(): void { $csIndex = []; $CSNS = 'http://purl.org/vocab/changeset/schema#'; - // Get the triples to be added - if (empty($this->before)) { - $additions = $this->after; - } else { - $additions = ExtendedGraph::diff($this->after, $this->before); - } + $additions = empty($this->before) ? $this->after : ExtendedGraph::diff($this->after, $this->before); + // Get the triples to be removed - if (empty($this->after)) { - $removals = $this->before; - } else { - $removals = ExtendedGraph::diff($this->before, $this->after); - } + $removals = empty($this->after) ? $this->before : ExtendedGraph::diff($this->before, $this->after); // remove etag triples foreach (['removals' => $removals, 'additions' => $additions] as $name => $graph) { foreach ($graph as $uri => $properties) { if (isset($properties['http://schemas.talis.com/2005/dir/schema#etag'])) { unset(${$name}[$uri]['http://schemas.talis.com/2005/dir/schema#etag']); - if (count(${$name}[$uri]) == 0) { + if (count(${$name}[$uri]) === 0) { unset(${$name}[$uri]); } } @@ -118,12 +123,12 @@ protected function __init() } // Get an array of all the subject uris - $subjectIndex = !empty($this->a['subjectOfChange']) ? [$this->a['subjectOfChange']] : array_unique(array_merge(array_keys($additions), array_keys($removals))); + $subjectIndex = empty($this->a['subjectOfChange']) ? array_unique(array_merge(array_keys($additions), array_keys($removals))) : [$this->a['subjectOfChange']]; // Get the metadata for all the changesets - $date = (!empty($this->a['createdDate'])) ? $this->a['createdDate'] : date(DATE_ATOM); - $creator = (!empty($this->a['creatorName'])) ? $this->a['creatorName'] : 'Moriarty ChangeSet Builder'; - $reason = (!empty($this->a['changeReason'])) ? $this->a['changeReason'] : 'Change using Moriarty ChangeSet Builder'; + $date = (empty($this->a['createdDate'])) ? date(DATE_ATOM) : $this->a['createdDate']; + $creator = (empty($this->a['creatorName'])) ? 'Moriarty ChangeSet Builder' : $this->a['creatorName']; + $reason = (empty($this->a['changeReason'])) ? 'Change using Moriarty ChangeSet Builder' : $this->a['changeReason']; $csCount = 0; foreach ($subjectIndex as $subjectOfChange) { @@ -142,25 +147,25 @@ protected function __init() $this->addT($csID, $p, $objs); } } + $csCount++; } + /*iterate through the triples to be added, reifying them, and linking to the Statements from the appropriate changeset */ $reifiedAdditions = ExtendedGraph::reify($additions, 'Add'); - if (!empty($reifiedAdditions)) { - foreach ($reifiedAdditions as $nodeID => $props) { - $subject = $props['http://www.w3.org/1999/02/22-rdf-syntax-ns#subject'][0]['value']; - if (in_array($subject, $subjectIndex)) { - $csID = $csIndex[$subject]; - $this->addT($csID, $CSNS . 'addition', $nodeID, 'bnode'); - } + foreach ($reifiedAdditions as $nodeID => $props) { + $subject = $props['http://www.w3.org/1999/02/22-rdf-syntax-ns#subject'][0]['value']; + if (in_array($subject, $subjectIndex)) { + $csID = $csIndex[$subject]; + $this->addT($csID, $CSNS . 'addition', $nodeID, 'bnode'); + } - // if dc:source is given in the instantiating arguments, add it to the statement as provenance - if (isset($this->a['http://purl.org/dc/terms/source'])) { - $this->addT($nodeID, 'http://purl.org/dc/terms/source', $this->a['http://purl.org/dc/terms/source'], 'uri'); - } + // if dc:source is given in the instantiating arguments, add it to the statement as provenance + if (isset($this->a['http://purl.org/dc/terms/source'])) { + $this->addT($nodeID, 'http://purl.org/dc/terms/source', $this->a['http://purl.org/dc/terms/source'], 'uri'); } } @@ -184,29 +189,26 @@ protected function __init() /** * adds a triple to the internal simpleIndex holding all the changesets and statements. * - * @param string $s Subject uri - * @param string $p Predicate URI - * @param string $o Object URI or literal value - * @param string $o_type + * @param TripleSubject $s Subject URI + * @param TriplePredicate $p Predicate URI + * @param string|TripleObject|TripleObject[] $o Object URI or literal value + * @param ObjectType $o_type Object type (bnode, uri, literal) * * @author Keith */ - public function addT($s, $p, $o, $o_type = 'bnode') + public function addT(string $s, string $p, $o, $o_type = 'bnode'): void { - if (is_array($o) and isset($o[0]['type'])) { + if (is_array($o) && isset($o[0]['type'])) { foreach ($o as $obj) { $this->addT($s, $p, $obj); } } else { - $obj = !is_array($o) ? ['value' => $o, 'type' => $o_type] : $o; + $obj = is_array($o) ? $o : ['value' => $o, 'type' => $o_type]; $this->_index[$s][$p][] = $obj; } } - /** - * @return string - */ - public function toRDFXML() + public function toRDFXML(): string { /** @var \ARC2_RDFSerializer $ser */ $ser = \ARC2::getRDFXMLSerializer(); @@ -214,23 +216,16 @@ public function toRDFXML() return $ser->getSerializedIndex($this->_index); } - /** - * @return string - */ - public function to_rdfxml() + public function to_rdfxml(): string { return $this->toRDFXML(); } - /** - * @return bool - */ - public function has_changes() + public function has_changes(): bool { foreach ($this->_index as $properties) { if ( - isset($properties['http://purl.org/vocab/changeset/schema#addition']) - or isset($properties['http://purl.org/vocab/changeset/schema#removal']) + isset($properties['http://purl.org/vocab/changeset/schema#addition']) || isset($properties['http://purl.org/vocab/changeset/schema#removal']) ) { return true; } @@ -241,10 +236,8 @@ public function has_changes() /** * Returns a unique array of the subjects of change in this changeset. - * - * @return array */ - public function get_subjects_of_change() + public function get_subjects_of_change(): array { $subjects = []; diff --git a/src/classes/ExtendedGraph.php b/src/classes/ExtendedGraph.php index 562b07f6..ff7d6689 100644 --- a/src/classes/ExtendedGraph.php +++ b/src/classes/ExtendedGraph.php @@ -1,5 +1,7 @@ > + * * @see https://code.google.com/p/moriarty/source/browse/trunk/labeller.class.php */ class ExtendedGraph { - // END SimpleGraph - - // Modifications start here - public const rdf = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; + public const rdf_type = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; + public const rdf_seq = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq'; - // FROM SimpleGraph - public $_index = []; - public $_image_properties = ['http://xmlns.com/foaf/0.1/depiction', 'http://xmlns.com/foaf/0.1/img']; - public $_property_order = ['http://www.w3.org/2004/02/skos/core#prefLabel', RDFS_LABEL, 'http://purl.org/dc/terms/title', DC_TITLE, FOAF_NAME, 'http://www.w3.org/2004/02/skos/core#definition', RDFS_COMMENT, 'http://purl.org/dc/terms/description', DC_DESCRIPTION, 'http://purl.org/vocab/bio/0.1/olb', RDF_TYPE]; - public $parser_errors = []; + + /** @var TripleGraph */ + public array $_index = []; + + /** @var string[] */ + public array $_image_properties = [ + 'http://xmlns.com/foaf/0.1/depiction', + 'http://xmlns.com/foaf/0.1/img', + ]; + + /** @var string[] */ + public array $_property_order = [ + 'http://www.w3.org/2004/02/skos/core#prefLabel', + RDFS_LABEL, + 'http://purl.org/dc/terms/title', + DC_TITLE, + FOAF_NAME, + 'http://www.w3.org/2004/02/skos/core#definition', + RDFS_COMMENT, + 'http://purl.org/dc/terms/description', + DC_DESCRIPTION, + 'http://purl.org/vocab/bio/0.1/olb', + RDF_TYPE, + ]; + + public array $parser_errors = []; /** * @var Labeller */ public $_labeller; - protected $_ns = [ + + /** @var array */ + protected array $_ns = [ 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', 'owl' => 'http://www.w3.org/2002/07/owl#', @@ -65,12 +97,12 @@ class ExtendedGraph /** * Up to the application to decide what constitures the label properties for a given app. * - * @var array + * @var string[] */ - private static $labelProperties; + private static array $labelProperties; /** - * @param array|string $graph + * @param string|TripleGraph $graph */ public function __construct($graph = null) { @@ -95,7 +127,7 @@ public function __destruct() * @param string $prefix the namespace prefix to associate with the URI * @param string $uri the URI to associate with the prefix */ - public function set_namespace_mapping($prefix, $uri) + public function set_namespace_mapping(string $prefix, string $uri): void { $this->_labeller->set_namespace_mapping($prefix, $uri); } @@ -103,41 +135,33 @@ public function set_namespace_mapping($prefix, $uri) /** * Convert a QName to a URI using registered namespace prefixes. * - * @param string $qname the QName to convert + * @param string|null $qName the QName to convert * - * @return string the URI corresponding to the QName if a suitable prefix exists, null otherwise + * @return string|null the URI corresponding to the QName if a suitable prefix exists, null otherwise */ - public function qname_to_uri($qname) + public function qname_to_uri(?string $qName): ?string { - return $this->_labeller->qname_to_uri($qname); + return $this->_labeller->qname_to_uri($qName); } /** * Convert a URI to a QName using registered namespace prefixes. * - * @param string $uri the URI to convert + * @param string|null $uri the URI to convert * - * @return string the QName corresponding to the URI if a suitable prefix exists, null otherwise + * @return string|null the QName corresponding to the URI if a suitable prefix exists, null otherwise */ - public function uri_to_qname($uri) + public function uri_to_qname(?string $uri): ?string { return $this->_labeller->uri_to_qname($uri); } - /** - * @param string $ns - * - * @return string - */ - public function get_prefix($ns) + public function get_prefix(string $ns): string { return $this->_labeller->get_prefix($ns); } - /** - * @param string $p - */ - public function add_labelling_property($p) + public function add_labelling_property(string $p): void { $this->_labeller->add_labelling_property($p); } @@ -147,9 +171,9 @@ public function add_labelling_property($p) * * @param string $resource a URI or blank node identifier (prefixed with _: e.g. _:name) * - * @return array an associative array with two keys: 'type' and 'value'. Type is either bnode or uri + * @return array an associative array with two keys: 'type' and 'value'. Type is either bnode or uri */ - public function make_resource_array($resource) + public function make_resource_array(string $resource): array { $resource_type = strpos($resource, '_:') === 0 ? 'bnode' : 'uri'; @@ -159,13 +183,13 @@ public function make_resource_array($resource) /** * Adds a triple with a resource object to the graph. * - * @param string $s the subject of the triple, either a URI or a blank node in the format _:name - * @param string $p the predicate URI of the triple - * @param string $o the object of the triple, either a URI or a blank node in the format _:name + * @param TripleSubject $s the subject of the triple, either a URI or a blank node in the format _:name + * @param TriplePredicate $p the predicate URI of the triple + * @param ObjectResource|null $o the object of the triple, either a URI or a blank node in the format _:name * * @return bool true if the triple was new, false if it already existed in the graph */ - public function add_resource_triple($s, $p, $o) + public function add_resource_triple(string $s, string $p, ?string $o): bool { if ($this->isValidResource($o)) { return $this->_add_triple($s, $p, ['type' => strpos($o, '_:') === 0 ? 'bnode' : 'uri', 'value' => $o]); @@ -177,21 +201,22 @@ public function add_resource_triple($s, $p, $o) /** * Adds a triple with a literal object to the graph. * - * @param string $s the subject of the triple, either a URI or a blank node in the format _:name - * @param string $p the predicate of the triple as a URI - * @param string $o the object of the triple as a string - * @param string $lang the language code of the triple's object (optional) - * @param string $dt the datatype URI of the triple's object (optional) + * @param TripleSubject $s the subject of the triple, either a URI or a blank node in the format _:name + * @param TriplePredicate $p the predicate of the triple as a URI + * @param ObjectLiteral $o the object of the triple as a scalar value + * @param string|null $lang the language code of the triple's object (optional) + * @param string|null $dt the datatype URI of the triple's object (optional) * * @return bool true if the triple was new, false if it already existed in the graph */ - public function add_literal_triple($s, $p, $o, $lang = null, $dt = null) + public function add_literal_triple(string $s, string $p, $o, ?string $lang = null, ?string $dt = null): bool { if ($this->isValidLiteral($o)) { $o_info = ['type' => 'literal', 'value' => $o]; if ($lang != null) { $o_info['lang'] = $lang; } + if ($dt != null) { $o_info['datatype'] = $dt; } @@ -205,7 +230,7 @@ public function add_literal_triple($s, $p, $o, $lang = null, $dt = null) /** * @deprecated this is deprecated */ - public function get_triples() + public function get_triples(): array { return \ARC2::getTriplesFromIndex($this->_to_arc_index($this->_index)); } @@ -213,9 +238,11 @@ public function get_triples() /** * Get a copy of the graph's triple index. * - * @see http://n2.talis.com/wiki/RDF_PHP_Specification + * @see https://www.easyrdf.org/docs/rdf-formats-php + * + * @return TripleGraph */ - public function get_index() + public function get_index(): array { return $this->_index; } @@ -225,7 +252,7 @@ public function get_index() * * @return string the RDF/XML version of the graph */ - public function to_rdfxml() + public function to_rdfxml(): string { /** @var \ARC2_RDFSerializer $serializer */ $serializer = \ARC2::getRDFXMLSerializer( @@ -244,7 +271,7 @@ public function to_rdfxml() * * @return string the Turtle version of the graph */ - public function to_turtle() + public function to_turtle(): string { /** @var \ARC2_RDFSerializer $serializer */ $serializer = \ARC2::getTurtleSerializer( @@ -263,7 +290,7 @@ public function to_turtle() * * @return string the N-Triples version of the graph */ - public function to_ntriples() + public function to_ntriples(): string { /** @var \ARC2_RDFSerializer $serializer */ $serializer = \ARC2::getComponent('NTriplesSerializer', []); @@ -274,11 +301,11 @@ public function to_ntriples() /** * Serialise the graph to JSON. * - * @see http://n2.talis.com/wiki/RDF_JSON_Specification + * @see https://www.easyrdf.org/docs/rdf-formats-json * * @return string the JSON version of the graph */ - public function to_json() + public function to_json(): string { return json_encode($this->_index); } @@ -286,104 +313,107 @@ public function to_json() /** * Serialise the graph to HTML. * - * @param array|string|null $s - * @param bool $guess_labels + * @param TripleSubject|TripleSubject[]|null $s * * @return string a HTML version of the graph */ - public function to_html($s = null, $guess_labels = true) + public function to_html($s = null, bool $guess_labels = true): string { $h = ''; if ($s) { if (is_array($s)) { $subjects = array_intersect($s, $this->get_subjects()); - if (count($subjects) == 0) { + if ($subjects === []) { return ''; } + } elseif (isset($this->_index[$s])) { + $subjects = [$s]; } else { - if (array_key_exists($s, $this->_index)) { - $subjects = [$s]; - } else { - return ''; - } + return ''; } } else { $subjects = $this->get_subjects(); } - if (count($subjects) > 0) { - foreach ($subjects as $subject) { - if (count($subjects) > 1) { - $h .= '

' . htmlspecialchars($this->get_label($subject)) . '

' . "\n"; - } - $h .= '' . "\n"; - - $properties = $this->get_subject_properties($subject, true); - $priority_properties = array_intersect($properties, $this->_property_order); - $properties = array_merge($priority_properties, array_diff($properties, $priority_properties)); - - foreach ($properties as $p) { - $h .= ''; - $h .= ''; + $h .= '' . "\n"; } + + $h .= '
' . htmlspecialchars($this->get_label($p, true)) . ''; - for ($i = 0; $i < count($this->_index[$subject][$p]); $i++) { - if ($i > 0) { - $h .= '
'; - } - if ($this->_index[$subject][$p][$i]['type'] === 'literal') { - $h .= htmlspecialchars($this->_index[$subject][$p][$i]['value']); - } else { - $h .= ''; - if ($guess_labels) { - $h .= htmlspecialchars($this->get_label($this->_index[$subject][$p][$i]['value'])); - } else { - $h .= htmlspecialchars($this->_index[$subject][$p][$i]['value']); - } + foreach ($subjects as $subject) { + if (count($subjects) > 1) { + $h .= '

' . htmlspecialchars($this->get_label($subject)) . '

' . "\n"; + } - $h .= ''; + $h .= '' . "\n"; + + $properties = $this->get_subject_properties($subject, true); + $priority_properties = array_intersect($properties, $this->_property_order); + $properties = array_merge($priority_properties, array_diff($properties, $priority_properties)); + + foreach ($properties as $p) { + $h .= ''; + $h .= ''; - $h .= '' . "\n"; } - $backlinks = []; - foreach ($this->_index as $rev_subj => $rev_subj_info) { - foreach ($rev_subj_info as $rev_subj_p => $rev_subj_p_list) { - foreach ($rev_subj_p_list as $rev_value) { - if (($rev_value['type'] == 'uri' || $rev_value['type'] == 'bnode') && $rev_value['value'] === $subject) { - if (!isset($backlinks[$rev_subj_p])) { - $backlinks[$rev_subj_p] = []; - } - $backlinks[$rev_subj_p][] = $rev_subj; + $h .= ''; + $h .= '' . "\n"; + } + + $backlinks = []; + foreach ($this->_index as $rev_subj => $rev_subj_info) { + foreach ($rev_subj_info as $rev_subj_p => $rev_subj_p_list) { + foreach ($rev_subj_p_list as $rev_value) { + if (($rev_value['type'] == 'uri' || $rev_value['type'] == 'bnode') && $rev_value['value'] === $subject) { + if (!isset($backlinks[$rev_subj_p])) { + $backlinks[$rev_subj_p] = []; } + + $backlinks[$rev_subj_p][] = $rev_subj; } } } + } - foreach ($backlinks as $backlink_p => $backlink_values) { - $h .= ''; - $h .= ''; + $h .= ''; - $h .= '' . "\n"; + + $h .= ''; } - $h .= '
' . htmlspecialchars($this->get_label($p, true)) . ''; + $counter = count($this->_index[$subject][$p]); + for ($i = 0; $i < $counter; $i++) { + if ($i > 0) { + $h .= '
'; + } + + $value = (string) $this->_index[$subject][$p][$i]['value']; + if ($this->_index[$subject][$p][$i]['type'] === 'literal') { + $h .= htmlspecialchars($value); + } else { + $h .= ''; + if ($guess_labels) { + $h .= htmlspecialchars($this->get_label($value)); + } else { + $h .= htmlspecialchars($value); } + + $h .= ''; } - $h .= '
' . htmlspecialchars($this->get_inverse_label($backlink_p, true)) . ''; - for ($i = 0; $i < count($backlink_values); $i++) { - if ($i > 0) { - $h .= '
'; - } - - $h .= ''; - if ($guess_labels) { - $h .= htmlspecialchars($this->get_label($backlink_values[$i])); - } else { - $h .= htmlspecialchars($backlink_values[$i]); - } + foreach ($backlinks as $backlink_p => $backlink_values) { + $h .= '
' . htmlspecialchars($this->get_inverse_label($backlink_p, true)) . ''; + $counter = count($backlink_values); + for ($i = 0; $i < $counter; $i++) { + if ($i > 0) { + $h .= '
'; + } - $h .= ''; + $h .= ''; + if ($guess_labels) { + $h .= htmlspecialchars($this->get_label($backlink_values[$i])); + } else { + $h .= htmlspecialchars($backlink_values[$i]); } - $h .= '
' . "\n"; + $h .= '
' . "\n"; } return $h; @@ -392,27 +422,26 @@ public function to_html($s = null, $guess_labels = true) /** * Fetch the first literal value for a given subject and predicate. If there are multiple possible values then one is selected at random. * - * @param string $s the subject to search for - * @param string $p the predicate to search for, or an array of predicates - * @param string $default a default value to use if no literal values are found - * @param string $preferred_language + * @param TripleSubject $s the subject to search for + * @param TriplePredicate|TriplePredicate[] $p the predicate to search for, or an array of predicates + * @param ObjectLiteral|null $default a default value to use if no literal values are found * - * @return string the first literal value found or the supplied default if no values were found + * @return ObjectLiteral|null the first literal value found or the supplied default if no values were found */ - public function get_first_literal($s, $p, $default = null, $preferred_language = null) + public function get_first_literal(string $s, $p, $default = null, ?string $preferred_language = null) { $best_literal = $default; - if (array_key_exists($s, $this->_index)) { + if (isset($this->_index[$s])) { if (is_array($p)) { foreach ($p as $p_uri) { - if (array_key_exists($p_uri, $this->_index[$s])) { + if (isset($this->_index[$s][$p_uri])) { foreach ($this->_index[$s][$p_uri] as $value) { if ($value['type'] == 'literal') { if ($preferred_language == null) { return $value['value']; } - if (array_key_exists('lang', $value) && $value['lang'] == $preferred_language) { + if (isset($value['lang']) && $value['lang'] == $preferred_language) { return $value['value']; } @@ -421,14 +450,14 @@ public function get_first_literal($s, $p, $default = null, $preferred_language = } } } - } elseif (array_key_exists($p, $this->_index[$s])) { + } elseif (isset($this->_index[$s][$p])) { foreach ($this->_index[$s][$p] as $value) { if ($value['type'] == 'literal') { if ($preferred_language == null) { return $value['value']; } - if (array_key_exists('lang', $value) && $value['lang'] == $preferred_language) { + if (isset($value['lang']) && $value['lang'] == $preferred_language) { return $value['value']; } @@ -444,13 +473,13 @@ public function get_first_literal($s, $p, $default = null, $preferred_language = /** * Fetch the first resource value for a given subject and predicate. If there are multiple possible values then one is selected at random. * - * @param string $s the subject to search for - * @param string $p the predicate to search for - * @param string $default a default value to use if no literal values are found + * @param TripleSubject $s the subject to search for + * @param TriplePredicate $p the predicate to search for + * @param ObjectResource $default a default value to use if no literal values are found * - * @return string the first resource value found or the supplied default if no values were found + * @return ObjectResource|null the first resource value found or the supplied default if no values were found */ - public function get_first_resource($s, $p, $default = null) + public function get_first_resource(string $s, string $p, ?string $default = null): ?string { if (isset($this->_index[$s][$p])) { foreach ($this->_index[$s][$p] as $value) { @@ -466,11 +495,11 @@ public function get_first_resource($s, $p, $default = null) /** * Remove a triple with a resource object from the graph. * - * @param string $s the subject of the triple, either a URI or a blank node in the format _:name - * @param string $p the predicate URI of the triple - * @param string $o the object of the triple, either a URI or a blank node in the format _:name + * @param TripleSubject $s the subject of the triple, either a URI or a blank node in the format _:name + * @param TriplePredicate $p the predicate URI of the triple + * @param ObjectResource $o the object of the triple, either a URI or a blank node in the format _:name */ - public function remove_resource_triple($s, $p, $o) + public function remove_resource_triple(string $s, string $p, $o): void { // Already removed if (!isset($this->_index[$s]) || !isset($this->_index[$s][$p])) { @@ -483,21 +512,19 @@ public function remove_resource_triple($s, $p, $o) } } - if (count($this->_index[$s][$p]) == 0) { + if (count($this->_index[$s][$p]) === 0) { unset($this->_index[$s][$p]); } - if (count($this->_index[$s]) == 0) { + if (count($this->_index[$s]) === 0) { unset($this->_index[$s]); } } /** - * @param string $s - * @param string $p - * @param string $o + * @param ObjectLiteral $o */ - public function remove_literal_triple($s, $p, $o) + public function remove_literal_triple(string $s, string $p, $o): void { // Already removed if (!isset($this->_index[$s]) || !isset($this->_index[$s][$p])) { @@ -510,11 +537,11 @@ public function remove_literal_triple($s, $p, $o) } } - if (count($this->_index[$s][$p]) == 0) { + if (count($this->_index[$s][$p]) === 0) { unset($this->_index[$s][$p]); } - if (count($this->_index[$s]) == 0) { + if (count($this->_index[$s]) === 0) { unset($this->_index[$s]); } } @@ -522,9 +549,9 @@ public function remove_literal_triple($s, $p, $o) /** * Remove all triples having the supplied subject. * - * @param string $s the subject of the triple, either a URI or a blank node in the format _:name + * @param TripleSubject $s the subject of the triple, either a URI or a blank node in the format _:name */ - public function remove_triples_about($s) + public function remove_triples_about(string $s): void { unset($this->_index[$s]); } @@ -535,9 +562,9 @@ public function remove_triples_about($s) * @param string $rdfxml the RDF/XML to parse * @param string $base the base URI against which relative URIs in the RDF/XML document will be resolved */ - public function from_rdfxml($rdfxml, $base = '') + public function from_rdfxml(string $rdfxml, string $base = ''): void { - if ($rdfxml) { + if ($rdfxml !== '' && $rdfxml !== '0') { $this->remove_all_triples(); $this->add_rdfxml($rdfxml, $base); } @@ -546,13 +573,13 @@ public function from_rdfxml($rdfxml, $base = '') /** * Replace the triples in the graph with those parsed from the supplied JSON. * - * @see http://n2.talis.com/wiki/RDF_JSON_Specification + * @see https://www.easyrdf.org/docs/rdf-formats-json * * @param string $json the JSON to parse */ - public function from_json($json) + public function from_json(string $json): void { - if ($json) { + if ($json !== '' && $json !== '0') { $this->remove_all_triples(); $index = json_decode($json, true); if (is_array($index)) { @@ -564,13 +591,13 @@ public function from_json($json) /** * Add the triples parsed from the supplied JSON to the graph. * - * @see http://n2.talis.com/wiki/RDF_JSON_Specification + * @see https://www.easyrdf.org/docs/rdf-formats-json * * @param string $json the JSON to parse */ - public function add_json($json) + public function add_json(string $json): void { - if ($json) { + if ($json !== '' && $json !== '0') { $json_index = json_decode($json, true); if (is_array($json_index)) { $this->_index = $this->merge($this->_index, $json_index); @@ -578,10 +605,7 @@ public function add_json($json) } } - /** - * @return array - */ - public function get_parser_errors() + public function get_parser_errors(): array { return $this->parser_errors; } @@ -594,7 +618,7 @@ public function get_parser_errors() * * @author Keith Alexander */ - public function add_rdf($rdf, $base = '') + public function add_rdf(string $rdf, string $base = ''): void { $trimRdf = trim($rdf); if ($trimRdf[0] == '{') { // lazy is-this-json assessment - might be better to try json_decode - but more costly @@ -608,6 +632,7 @@ public function add_rdf($rdf, $base = '') if (!empty($errors)) { $this->parser_errors[] = $errors; } + $triples = $parser->getTriples(); $this->_add_arc2_triple_list($triples); unset($parser); @@ -620,9 +645,9 @@ public function add_rdf($rdf, $base = '') * @param string $rdfxml the RDF/XML to parse * @param string $base the base URI against which relative URIs in the RDF/XML document will be resolved */ - public function add_rdfxml($rdfxml, $base = '') + public function add_rdfxml(string $rdfxml, string $base = ''): void { - if ($rdfxml) { + if ($rdfxml !== '' && $rdfxml !== '0') { /** @var \ARC2_RDFXMLParser $parser */ $parser = \ARC2::getRDFXMLParser(); $parser->parse($base, $rdfxml); @@ -640,9 +665,9 @@ public function add_rdfxml($rdfxml, $base = '') * @param string $turtle the Turtle to parse * @param string $base the base URI against which relative URIs in the Turtle document will be resolved */ - public function from_turtle($turtle, $base = '') + public function from_turtle(string $turtle, string $base = ''): void { - if ($turtle) { + if ($turtle !== '' && $turtle !== '0') { $this->remove_all_triples(); $this->add_turtle($turtle, $base); } @@ -656,9 +681,9 @@ public function from_turtle($turtle, $base = '') * @param string $turtle the Turtle to parse * @param string $base the base URI against which relative URIs in the Turtle document will be resolved */ - public function add_turtle($turtle, $base = '') + public function add_turtle(string $turtle, string $base = ''): void { - if ($turtle) { + if ($turtle !== '' && $turtle !== '0') { /** @var \ARC2_TurtleParser $parser */ $parser = \ARC2::getTurtleParser(); $parser->parse($base, $turtle); @@ -672,11 +697,11 @@ public function add_turtle($turtle, $base = '') * Replace the triples in the graph with those parsed from the supplied RDFa. * * @param string $html the HTML containing RDFa to parse - * @param string $base the base URI against which relative URIs in the Turtle document will be resolved + * @param string $base the base URI against which relative URIs in the RDFa document will be resolved */ - public function from_rdfa($html, $base = '') + public function from_rdfa(string $html, string $base = ''): void { - if ($html) { + if ($html !== '' && $html !== '0') { $this->remove_all_triples(); $this->add_rdfa($html, $base); } @@ -686,11 +711,11 @@ public function from_rdfa($html, $base = '') * Add the triples parsed from the supplied RDFa to the graph. * * @param string $html the HTML containing RDFa to parse - * @param string $base the base URI against which relative URIs in the Turtle document will be resolved + * @param string $base the base URI against which relative URIs in the RDFa document will be resolved */ - public function add_rdfa($html, $base = '') + public function add_rdfa(string $html, string $base = ''): void { - if ($html) { + if ($html !== '' && $html !== '0') { /** @var \ARC2_SemHTMLParser $parser */ $parser = \ARC2::getSemHTMLParser(); $parser->parse($base, $html); @@ -705,10 +730,8 @@ public function add_rdfa($html, $base = '') * Add the triples in the supplied graph to the current graph. * * @param ExtendedGraph $g the graph to read - * - * @return bool */ - public function add_graph(ExtendedGraph $g) + public function add_graph(ExtendedGraph $g): bool { $triples_were_added = false; $index = $g->get_index(); @@ -728,20 +751,18 @@ public function add_graph(ExtendedGraph $g) /** * Tests whether the graph contains the given triple. * - * @param string $s the subject of the triple, either a URI or a blank node in the format _:name - * @param string $p the predicate URI of the triple - * @param string $o the object of the triple, either a URI or a blank node in the format _:name + * @param TripleSubject $s the subject of the triple, either a URI or a blank node in the format _:name + * @param TriplePredicate $p the predicate URI of the triple + * @param ObjectResource $o the object of the triple, either a URI or a blank node in the format _:name * * @return bool true if the triple exists in the graph, false otherwise */ - public function has_resource_triple($s, $p, $o) + public function has_resource_triple(string $s, string $p, $o): bool { - if (array_key_exists($s, $this->_index)) { - if (array_key_exists($p, $this->_index[$s])) { - foreach ($this->_index[$s][$p] as $value) { - if (($value['type'] == 'uri' || $value['type'] == 'bnode') && $value['value'] === $o) { - return true; - } + if (isset($this->_index[$s][$p])) { + foreach ($this->_index[$s][$p] as $value) { + if (($value['type'] == 'uri' || $value['type'] == 'bnode') && $value['value'] === $o) { + return true; } } } @@ -752,30 +773,28 @@ public function has_resource_triple($s, $p, $o) /** * Tests whether the graph contains the given triple. * - * @param string $s the subject of the triple, either a URI or a blank node in the format _:name - * @param string $p the predicate URI of the triple - * @param string $o the object of the triple as a literal value - * @param string|null $lang the language of the object - * @param string|null $dt the datatype of the object + * @param TripleSubject $s the subject of the triple, either a URI or a blank node in the format _:name + * @param TriplePredicate $p the predicate URI of the triple + * @param ObjectLiteral $o the object of the triple as a literal value + * @param string|null $lang the language of the object + * @param string|null $dt the datatype of the object * * @return bool true if the triple exists in the graph, false otherwise */ - public function has_literal_triple($s, $p, $o, $lang = null, $dt = null) + public function has_literal_triple(string $s, string $p, $o, ?string $lang = null, ?string $dt = null): bool { - if (array_key_exists($s, $this->_index)) { - if (array_key_exists($p, $this->_index[$s])) { - foreach ($this->_index[$s][$p] as $value) { - if (($value['type'] == 'literal') && $value['value'] === $o) { - if ($lang !== null) { - return array_key_exists('lang', $value) && $value['lang'] === $lang; - } - - if ($dt !== null) { - return array_key_exists('datatype', $value) && $value['datatype'] === $dt; - } + if (isset($this->_index[$s][$p])) { + foreach ($this->_index[$s][$p] as $value) { + if (($value['type'] == 'literal') && $value['value'] === $o) { + if ($lang !== null) { + return isset($value['lang']) && $value['lang'] === $lang; + } - return true; + if ($dt !== null) { + return isset($value['datatype']) && $value['datatype'] === $dt; } + + return true; } } } @@ -786,20 +805,18 @@ public function has_literal_triple($s, $p, $o, $lang = null, $dt = null) /** * Fetch the resource values for a given subject and predicate. * - * @param string $s the subject to search for - * @param string $p the predicate to search for + * @param TripleSubject $s the subject to search for + * @param TriplePredicate $p the predicate to search for * - * @return array list of URIs and blank nodes that are the objects of triples with the supplied subject and predicate + * @return ObjectResource[] list of URIs and blank nodes that are the objects of triples with the supplied subject and predicate */ - public function get_resource_triple_values($s, $p) + public function get_resource_triple_values(string $s, string $p): array { $values = []; - if (array_key_exists($s, $this->_index)) { - if (array_key_exists($p, $this->_index[$s])) { - foreach ($this->_index[$s][$p] as $value) { - if ($value['type'] == 'uri' || $value['type'] == 'bnode') { - $values[] = $value['value']; - } + if (isset($this->_index[$s][$p])) { + foreach ($this->_index[$s][$p] as $value) { + if ($value['type'] == 'uri' || $value['type'] == 'bnode') { + $values[] = $value['value']; } } } @@ -810,18 +827,18 @@ public function get_resource_triple_values($s, $p) /** * Fetch the literal values for a given subject and predicate. * - * @param string $s the subject to search for - * @param string $p the predicate to search for + * @param TripleSubject $s the subject to search for + * @param TriplePredicate|TriplePredicate[] $p the predicate to search for or an array of predicates * - * @return array list of literals that are the objects of triples with the supplied subject and predicate + * @return ObjectLiteral[] list of literals that are the objects of triples with the supplied subject and predicate */ - public function get_literal_triple_values($s, $p) + public function get_literal_triple_values(string $s, $p): array { $values = []; - if (array_key_exists($s, $this->_index)) { + if (isset($this->_index[$s])) { if (is_array($p)) { foreach ($p as $p_uri) { - if (array_key_exists($p_uri, $this->_index[$s])) { + if (isset($this->_index[$s][$p_uri])) { foreach ($this->_index[$s][$p_uri] as $value) { if ($value['type'] == 'literal') { $values[] = $value['value']; @@ -829,7 +846,7 @@ public function get_literal_triple_values($s, $p) } } } - } elseif (array_key_exists($p, $this->_index[$s])) { + } elseif (isset($this->_index[$s][$p])) { foreach ($this->_index[$s][$p] as $value) { if ($value['type'] == 'literal') { $values[] = $value['value']; @@ -844,20 +861,21 @@ public function get_literal_triple_values($s, $p) /** * Fetch the values for a given subject and predicate. * - * @param string $s the subject to search for - * @param string $p the predicate to search for + * @param TripleSubject $s the subject to search for + * @param TriplePredicate|TriplePredicate[] $p the predicate to search for or an array of predicates * - * @return array list of values of triples with the supplied subject and predicate + * @return TripleObject[] list of values of triples with the supplied subject and predicate */ - public function get_subject_property_values($s, $p) + public function get_subject_property_values(string $s, $p): array { $values = []; if (!is_array($p)) { $p = [$p]; } - if (array_key_exists($s, $this->_index)) { + + if (isset($this->_index[$s])) { foreach ($p as $pinst) { - if (array_key_exists($pinst, $this->_index[$s])) { + if (isset($this->_index[$s][$pinst])) { foreach ($this->_index[$s][$pinst] as $value) { $values[] = $value; } @@ -871,14 +889,14 @@ public function get_subject_property_values($s, $p) /** * Fetch a subgraph where all triples have given subject. * - * @param string $s the subject to search for + * @param TripleSubject $s the subject to search for * * @return ExtendedGraph triples with the supplied subject */ - public function get_subject_subgraph($s) + public function get_subject_subgraph(string $s): ExtendedGraph { $sub = new ExtendedGraph(); - if (array_key_exists($s, $this->_index)) { + if (isset($this->_index[$s])) { $sub->_index[$s] = $this->_index[$s]; } @@ -888,9 +906,9 @@ public function get_subject_subgraph($s) /** * Fetch an array of all the subjects. * - * @return array + * @return TripleSubject[] list of all the subjects in the graph */ - public function get_subjects() + public function get_subjects(): array { return array_keys($this->_index); } @@ -898,24 +916,20 @@ public function get_subjects() /** * Fetch an array of all the subject that have and rdf type that matches that given. * - * @param string $t the type to match - * - * @return array + * @param ObjectResource $o the type to match */ - public function get_subjects_of_type($t) + public function get_subjects_of_type(string $o): array { - return $this->get_subjects_where_resource('http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $t); + return $this->get_subjects_where_resource('http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $o); } /** * Fetch an array of all the subjects where the predicate and object match a ?s $p $o triple in the graph and the object is a resource. * - * @param string $p the predicate to match - * @param string $o the resource object to match - * - * @return array + * @param TriplePredicate $p the predicate to match + * @param ObjectResource $o the resource object to match */ - public function get_subjects_where_resource($p, $o) + public function get_subjects_where_resource(string $p, string $o): array { return array_merge($this->get_subjects_where($p, $o, 'uri'), $this->get_subjects_where($p, $o, 'bnode')); } @@ -923,12 +937,10 @@ public function get_subjects_where_resource($p, $o) /** * Fetch an array of all the subjects where the predicate and object match a ?s $p $o triple in the graph and the object is a literal value. * - * @param string $p the predicate to match - * @param string $o the resource object to match - * - * @return array + * @param TriplePredicate $p the predicate to match + * @param ObjectValue $o the literal object to match */ - public function get_subjects_where_literal($p, $o) + public function get_subjects_where_literal(string $p, $o): array { return $this->get_subjects_where($p, $o, 'literal'); } @@ -936,20 +948,21 @@ public function get_subjects_where_literal($p, $o) /** * Fetch the properties of a given subject and predicate. * - * @param string $s the subject to search for - * @param bool $distinct if true then duplicate properties are included only once (optional, default is true) + * @param TripleSubject $s the subject to search for + * @param bool $distinct if true then duplicate properties are included only once (optional, default is true) * - * @return array list of property URIs + * @return TriplePredicate[] list of property URIs */ - public function get_subject_properties($s, $distinct = true) + public function get_subject_properties(string $s, bool $distinct = true): array { $values = []; - if (array_key_exists($s, $this->_index)) { + if (isset($this->_index[$s])) { foreach ($this->_index[$s] as $prop => $prop_values) { if ($distinct) { $values[] = $prop; } else { - for ($i = 0; $i < count($prop_values); $i++) { + $counter = count($prop_values); + for ($i = 0; $i < $counter; $i++) { $values[] = $prop; } } @@ -962,39 +975,35 @@ public function get_subject_properties($s, $distinct = true) /** * Tests whether the graph contains a triple with the given subject and predicate. * - * @param string $s the subject of the triple, either a URI or a blank node in the format _:name - * @param string $p the predicate URI of the triple + * @param TripleSubject $s the subject of the triple, either a URI or a blank node in the format _:name + * @param TriplePredicate $p the predicate URI of the triple * * @return bool true if a matching triple exists in the graph, false otherwise */ - public function subject_has_property($s, $p) + public function subject_has_property(string $s, string $p): bool { - if (array_key_exists($s, $this->_index)) { - return array_key_exists($p, $this->_index[$s]); - } - - return false; + return isset($this->_index[$s][$p]); } /** * Tests whether the graph contains a triple with the given subject. * - * @param string $s the subject of the triple, either a URI or a blank node in the format _:name + * @param TripleSubject $s the subject of the triple, either a URI or a blank node in the format _:name * * @return bool true if the graph contains any triples with the specified subject, false otherwise */ - public function has_triples_about($s) + public function has_triples_about(string $s): bool { - return array_key_exists($s, $this->_index); + return isset($this->_index[$s]); } /** * Removes all triples with the given subject and predicate. * - * @param string $s the subject of the triple, either a URI or a blank node in the format _:name - * @param string $p the predicate URI of the triple + * @param TripleSubject $s the subject of the triple, either a URI or a blank node in the format _:name + * @param TriplePredicate $p the predicate URI of the triple */ - public function remove_property_values($s, $p) + public function remove_property_values(string $s, string $p): void { unset($this->_index[$s][$p]); } @@ -1002,7 +1011,7 @@ public function remove_property_values($s, $p) /** * Clears all triples out of the graph. */ - public function remove_all_triples() + public function remove_all_triples(): void { $this->_index = []; } @@ -1012,41 +1021,25 @@ public function remove_all_triples() * * @return bool true if the graph contains no triples, false otherwise */ - public function is_empty() + public function is_empty(): bool { - return count($this->_index) == 0; + return count($this->_index) === 0; } - /** - * @param string $resource_uri - * @param bool $capitalize - * @param bool $use_qnames - * - * @return string - */ - public function get_label($resource_uri, $capitalize = false, $use_qnames = false) + public function get_label(string $resource_uri, bool $capitalize = false, bool $use_qnames = false): string { return $this->_labeller->get_label($resource_uri, $this, $capitalize, $use_qnames); } - /** - * @param string $resource_uri - * @param bool $capitalize - * @param bool $use_qnames - * - * @return string - */ - public function get_inverse_label($resource_uri, $capitalize = false, $use_qnames = false) + public function get_inverse_label(string $resource_uri, bool $capitalize = false, bool $use_qnames = false): string { return $this->_labeller->get_inverse_label($resource_uri, $this, $capitalize, $use_qnames); } /** - * @param string $nodeID_prefix - * - * @return array + * @param TripleGraph $resources */ - public function reify(array $resources, $nodeID_prefix = 'Statement') + public function reify(array $resources, string $nodeID_prefix = 'Statement'): array { $RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; $reified = []; @@ -1054,9 +1047,10 @@ public function reify(array $resources, $nodeID_prefix = 'Statement') foreach ($resources as $uri => $properties) { foreach ($properties as $property => $objects) { foreach ($objects as $object) { - while (!isset($statement_nodeID) or isset($resources[$statement_nodeID]) or isset($reified[$statement_nodeID])) { + while (!isset($statement_nodeID) || isset($resources[$statement_nodeID]) || isset($reified[$statement_nodeID])) { $statement_nodeID = '_:' . $nodeID_prefix . ($statement_no++); } + $reified[$statement_nodeID] = [ $RDF . 'type' => [ ['type' => 'uri', 'value' => $RDF . 'Statement'], @@ -1073,25 +1067,23 @@ public function reify(array $resources, $nodeID_prefix = 'Statement') } /** - * diff - * returns a simpleIndex consisting of all the statements in array1 that weren't found in any of the subsequent arrays. + * returns a simpleIndex consisting of all the statements from the first array that weren't found in any of the subsequent arrays. * - * @param array1, array2, [array3, ...] - * - * @return array + * @param TripleGraph ...$indices If only one array is passed then the diff is taken against the graph's own index, otherwise the diff is taken against the first array passed as a parameter * * @author Keith */ - public function diff() + public function diff(array ...$indices): array { - $indices = func_get_args(); - if (count($indices) == 1) { + if (count($indices) === 1) { array_unshift($indices, $this->_index); } + $base = array_shift($indices); if (count($base) === 0) { return []; } + $diff = []; foreach ($base as $base_uri => $base_ps) { @@ -1112,6 +1104,7 @@ public function diff() foreach ($base_p_values as &$v) { ksort($v); } + if (!in_array($base_o, $base_p_values, true)) { $diff[$base_uri][$base_p][] = $base_o; } @@ -1129,17 +1122,14 @@ public function diff() * merge * merges all rdf/json-style arrays passed as parameters. * - * @param array1, array2, [array3, ...] - * - * @return array + * @param TripleGraph ...$indices If only one array is passed then the merge is done against the graph's own index, otherwise the merge is done against the first array passed as a parameter * * @author Keith */ - public function merge() + public function merge(array ...$indices): array { $old_bnodeids = []; - $indices = func_get_args(); - if (count($indices) == 1) { + if (count($indices) === 1) { array_unshift($indices, $this->_index); } @@ -1149,13 +1139,12 @@ public function merge() /* Make sure that bnode ids don't overlap: _:a in g1 isn't the same as _:a in g2 */ - if (substr($uri, 0, 2) == '_:') {// bnode + if (substr($uri, 0, 2) == '_:') { // bnode $old_id = $uri; $count = 1; - while (isset($current[$uri]) - or ($old_id != $uri and isset($newGraph[$uri])) - or isset($old_bnodeids[$uri]) + while ( + isset($current[$uri]) || $old_id != $uri && isset($newGraph[$uri]) || isset($old_bnodeids[$uri]) ) { $uri .= $count++; } @@ -1165,7 +1154,7 @@ public function merge() } } - if (isset($properties) && is_array($properties)) { + if (!empty($properties)) { foreach ($properties as $property => $objects) { foreach ($objects as $object) { // make sure that the new bnode is being used @@ -1177,9 +1166,8 @@ public function merge() } else { // bnode hasn't been transposed $old_bnode_id = $bnode; $count = 1; - while (isset($current[$bnode]) - or ($object['value'] != $bnode and isset($newGraph[$bnode])) - or isset($old_bnodeids[$uri]) + while ( + isset($current[$bnode]) || $object['value'] != $bnode && isset($newGraph[$bnode]) || isset($old_bnodeids[$uri]) ) { $bnode .= $count++; } @@ -1187,11 +1175,12 @@ public function merge() if ($old_bnode_id != $bnode) { $old_bnodeids[$old_bnode_id] = $bnode; } + $object['value'] = $bnode; } } - if (!isset($current[$uri][$property]) or !in_array($object, $current[$uri][$property])) { + if (!isset($current[$uri][$property]) || !in_array($object, $current[$uri][$property])) { $current[$uri][$property][] = $object; } } @@ -1203,11 +1192,7 @@ public function merge() return $current; } - /** - * @param string $look_for - * @param string $replace_with - */ - public function replace_resource($look_for, $replace_with) + public function replace_resource(string $look_for, string $replace_with): void { $remove_list_resources = []; $remove_list_literals = []; @@ -1219,37 +1204,31 @@ public function replace_resource($look_for, $replace_with) if ($p == $look_for) { foreach ($o_list as $o_info) { if ($o_info['type'] == 'literal') { - $lang = array_key_exists('lang', $o_info) ? $o_info['lang'] : null; - $dt = array_key_exists('datatype', $o_info) ? $o_info['datatype'] : null; - + $lang = $o_info['lang'] ?? null; + $dt = $o_info['datatype'] ?? null; $remove_list_literals[] = [$look_for, $look_for, $o_info['value']]; $add_list_literals[] = [$replace_with, $replace_with, $o_info['value'], $lang, $dt]; + } elseif ($o_info['value'] == $look_for) { + $remove_list_resources[] = [$look_for, $look_for, $look_for]; + $add_list_resources[] = [$replace_with, $replace_with, $replace_with]; } else { - if ($o_info['value'] == $look_for) { - $remove_list_resources[] = [$look_for, $look_for, $look_for]; - $add_list_resources[] = [$replace_with, $replace_with, $replace_with]; - } else { - $remove_list_resources[] = [$look_for, $look_for, $o_info['value']]; - $add_list_resources[] = [$replace_with, $replace_with, $o_info['value']]; - } + $remove_list_resources[] = [$look_for, $look_for, $o_info['value']]; + $add_list_resources[] = [$replace_with, $replace_with, $o_info['value']]; } } } else { foreach ($o_list as $o_info) { if ($o_info['type'] == 'literal') { - $lang = array_key_exists('lang', $o_info) ? $o_info['lang'] : null; - $dt = array_key_exists('datatype', $o_info) ? $o_info['datatype'] : null; - + $lang = $o_info['lang'] ?? null; + $dt = $o_info['datatype'] ?? null; $remove_list_literals[] = [$look_for, $p, $o_info['value']]; $add_list_literals[] = [$replace_with, $p, $o_info['value'], $lang, $dt]; + } elseif ($o_info['value'] == $look_for) { + $remove_list_resources[] = [$look_for, $p, $look_for]; + $add_list_resources[] = [$replace_with, $p, $replace_with]; } else { - if ($o_info['value'] == $look_for) { - $remove_list_resources[] = [$look_for, $p, $look_for]; - $add_list_resources[] = [$replace_with, $p, $replace_with]; - } else { - $remove_list_resources[] = [$look_for, $p, $o_info['value']]; - $add_list_resources[] = [$replace_with, $p, $o_info['value']]; - } + $remove_list_resources[] = [$look_for, $p, $o_info['value']]; + $add_list_resources[] = [$replace_with, $p, $o_info['value']]; } } } @@ -1259,19 +1238,16 @@ public function replace_resource($look_for, $replace_with) if ($p == $look_for) { foreach ($o_list as $o_info) { if ($o_info['type'] == 'literal') { - $lang = array_key_exists('lang', $o_info) ? $o_info['lang'] : null; - $dt = array_key_exists('datatype', $o_info) ? $o_info['datatype'] : null; - + $lang = $o_info['lang'] ?? null; + $dt = $o_info['datatype'] ?? null; $remove_list_literals[] = [$s, $look_for, $o_info['value']]; $add_list_literals[] = [$s, $replace_with, $o_info['value'], $lang, $dt]; + } elseif ($o_info['value'] == $look_for) { + $remove_list_resources[] = [$s, $look_for, $look_for]; + $add_list_resources[] = [$s, $replace_with, $replace_with]; } else { - if ($o_info['value'] == $look_for) { - $remove_list_resources[] = [$s, $look_for, $look_for]; - $add_list_resources[] = [$s, $replace_with, $replace_with]; - } else { - $remove_list_resources[] = [$s, $look_for, $o_info['value']]; - $add_list_resources[] = [$s, $replace_with, $o_info['value']]; - } + $remove_list_resources[] = [$s, $look_for, $o_info['value']]; + $add_list_resources[] = [$s, $replace_with, $o_info['value']]; } } } else { @@ -1289,6 +1265,7 @@ public function replace_resource($look_for, $replace_with) foreach ($remove_list_resources as $t) { $this->remove_resource_triple($t[0], $t[1], $t[2]); } + foreach ($add_list_resources as $t) { $this->add_resource_triple($t[0], $t[1], $t[2]); } @@ -1296,20 +1273,16 @@ public function replace_resource($look_for, $replace_with) foreach ($remove_list_literals as $t) { $this->remove_literal_triple($t[0], $t[1], $t[2]); } + foreach ($add_list_literals as $t) { $this->add_literal_triple($t[0], $t[1], $t[2], $t[3], $t[4]); } } - /** - * @param string $listUri - * - * @return array - */ - public function get_list_values($listUri) + public function get_list_values(string $listUri): array { $array = []; - while (!empty($listUri) and $listUri != RDF_NIL) { + while (!empty($listUri) && $listUri !== RDF_NIL) { $array[] = $this->get_first_resource($listUri, RDF_FIRST); $listUri = $this->get_first_resource($listUri, RDF_REST); } @@ -1317,20 +1290,20 @@ public function get_list_values($listUri) return $array; } - public static function initProperties(array $properties) + /** + * @param array $properties + */ + public static function initProperties(array $properties): void { if (array_key_exists('labelProperties', $properties)) { - self::$labelProperties = $properties['labelProperties']; + self::$labelProperties = $properties['labelProperties'] ?? []; } } /** * Replaces $uri1 with $uri2 in subject, predicate and object position. - * - * @param string $uri1 - * @param string $uri2 */ - public function replace_uris($uri1, $uri2) + public function replace_uris(string $uri1, string $uri2): void { $index = $this->get_index(); if (isset($index[$uri1])) { @@ -1345,28 +1318,28 @@ public function replace_uris($uri1, $uri2) $property = $uri2; unset($index[$uri][$uri1]); } + foreach ($objects as $i => $object) { - if ($object['value'] == $uri1 and $object['type'] !== 'literal') { + if ($object['value'] == $uri1 && $object['type'] !== 'literal') { $index[$uri][$property][$i]['value'] = $uri2; } } } } + $this->_index = $index; } /** - * @param string|null $s - * @param string|null $p - * @param string|null $o - * - * @return int + * @param TripleSubject|null $s + * @param TriplePredicate|null $p + * @param ObjectValue|null $o */ - public function get_triple_count($s = null, $p = null, $o = null) + public function get_triple_count(?string $s = null, ?string $p = null, $o = null): int { $index = $this->get_index(); - if (empty($index)) { + if ($index === []) { return 0; } @@ -1392,9 +1365,9 @@ public function get_triple_count($s = null, $p = null, $o = null) /** * Fetch all the resource values for all subjects. * - * @return array the resource values found + * @return ObjectResource[] the resource values found */ - public function get_resources() + public function get_resources(): array { $resources = []; $subjects = $this->get_subjects(); @@ -1409,14 +1382,14 @@ public function get_resources() /** * Fetch all the resource values for a given subject. * - * @param string $s the subject to search for + * @param TripleSubject $s the subject to search for * - * @return array the resource values found + * @return ObjectResource[] the resource values found */ - public function get_resources_for_subject($s) + public function get_resources_for_subject(string $s): array { $resources = []; - if (array_key_exists($s, $this->_index)) { + if (isset($this->_index[$s])) { foreach ($this->_index[$s] as $values) { foreach ($values as $value) { if ($value['type'] == 'uri' || $value['type'] == 'bnode') { @@ -1430,9 +1403,9 @@ public function get_resources_for_subject($s) } /** - * @param string $p + * @param TriplePredicate $p */ - public function remove_properties($p) + public function remove_properties(string $p): void { foreach ($this->get_subjects() as $s) { $this->remove_property_values($s, $p); @@ -1440,11 +1413,11 @@ public function remove_properties($p) } /** - * @param string $p + * @param TriplePredicate $p * - * @return array + * @return ObjectResource[] the resource values found */ - public function get_resource_properties($p) + public function get_resource_properties(string $p): array { $resources = []; foreach ($this->get_subjects() as $s) { @@ -1456,12 +1429,12 @@ public function get_resource_properties($p) } /** - * @param string $p - * @param string $o + * @param TriplePredicate $p + * @param ObjectValue $o * - * @return array + * @return TripleSubject[] */ - public function get_subjects_with_property_value($p, $o) + public function get_subjects_with_property_value(string $p, $o): array { $subjects = []; foreach ($this->get_subjects() as $s) { @@ -1474,11 +1447,11 @@ public function get_subjects_with_property_value($p, $o) } /** - * @param string $sequenceUri + * @param TripleSubject $sequenceUri * - * @return array + * @return ObjectValue[] */ - public function get_sequence_values($sequenceUri) + public function get_sequence_values(string $sequenceUri): array { $triples = $this->get_index(); $properties = []; @@ -1500,21 +1473,13 @@ public function get_sequence_values($sequenceUri) ksort($properties, SORT_NUMERIC); } - $values = []; - - foreach ($properties as $value) { - $values[] = $value; - } - - return $values; + return array_values($properties); } /** - * @param string $sequenceUri - * - * @return int + * @param TripleSubject $sequenceUri */ - public function get_next_sequence($sequenceUri) + public function get_next_sequence(string $sequenceUri): int { $values = $this->get_sequence_values($sequenceUri); @@ -1522,10 +1487,10 @@ public function get_next_sequence($sequenceUri) } /** - * @param string $s - * @param string $o + * @param TripleSubject $s + * @param ObjectLiteral $o */ - public function add_literal_to_sequence($s, $o) + public function add_literal_to_sequence(string $s, $o): void { $this->add_to_sequence($s, $o, 'literal'); } @@ -1533,10 +1498,10 @@ public function add_literal_to_sequence($s, $o) /** * Remove a resource from a specified sequence and reindex the sequence to remove the gap. * - * @param string $sequenceUri - * @param string $resourceValue + * @param TripleSubject $sequenceUri + * @param ObjectResource $resourceValue */ - public function remove_resource_from_sequence($sequenceUri, $resourceValue) + public function remove_resource_from_sequence(string $sequenceUri, string $resourceValue): void { $sequenceProperties = $this->get_subject_properties($sequenceUri); $sequenceValues = $this->get_sequence_values($sequenceUri); @@ -1558,24 +1523,23 @@ public function remove_resource_from_sequence($sequenceUri, $resourceValue) } /** - * @param string $s - * @param string $o + * @param TripleSubject $s + * @param ObjectResource $o */ - public function add_resource_to_sequence($s, $o) + public function add_resource_to_sequence(string $s, string $o): void { - $this->add_to_sequence($s, $o); + $this->add_to_sequence($s, $o, 'resource'); } /** - * @param string $s - * @param string $o - * @param int $position + * @param TripleSubject $s + * @param ObjectResource $o */ - public function add_resource_to_sequence_in_position($s, $o, $position) + public function add_resource_to_sequence_in_position(string $s, string $o, int $position): void { $sequenceValues = $this->get_sequence_values($s); - if (empty($sequenceValues) || $position > count($sequenceValues)) { + if ($sequenceValues === [] || $position > count($sequenceValues)) { $this->add_resource_to_sequence($s, $o); } else { array_splice($sequenceValues, $position - 1, 1, [$o, $sequenceValues[$position - 1]]); @@ -1594,14 +1558,10 @@ public function add_resource_to_sequence_in_position($s, $o, $position) } /** - * @param string $s - * @param string $p - * @param string $oOldValue - * @param string $oNewValue - * - * @return bool + * @param ObjectLiteral $oOldValue + * @param ObjectLiteral $oNewValue */ - public function replace_literal_triple($s, $p, $oOldValue, $oNewValue) + public function replace_literal_triple(string $s, string $p, $oOldValue, $oNewValue): bool { if ($this->has_literal_triple($s, $p, $oOldValue)) { $this->remove_literal_triple($s, $p, $oOldValue); @@ -1614,50 +1574,52 @@ public function replace_literal_triple($s, $p, $oOldValue, $oNewValue) } /** - * @param string $s - * @param string $p - * @param string $o + * @param TripleSubject $s + * @param TriplePredicate $p + * @param ObjectResource|null $o */ - public function replace_resource_triples($s, $p, $o) + public function replace_resource_triples(string $s, string $p, ?string $o): void { if ($this->subject_has_property($s, $p)) { $this->remove_property_values($s, $p); } + if (!empty($o)) { $this->add_resource_triple($s, $p, $o); } } /** - * @param string $s - * @param string $p - * @param string $o + * @param TripleSubject $s + * @param TriplePredicate $p + * @param ObjectLiteral|null $o */ - public function replace_literal_triples($s, $p, $o) + public function replace_literal_triples(string $s, string $p, $o): void { if ($this->subject_has_property($s, $p)) { $this->remove_property_values($s, $p); } + if (!empty($o)) { $this->add_literal_triple($s, $p, $o); } } /** - * @param string $uri - * - * @return string + * @param TripleSubject $uri * * @throws Exception */ - public function get_label_for_uri($uri) + public function get_label_for_uri(string $uri): string { - if (!isset($this->_index[$uri])) { + if (empty($this->_index[$uri])) { return ''; } - if (!isset(self::$labelProperties)) { + + if (empty(self::$labelProperties)) { throw new Exception('Please initialise ExtendedGraph::$labelProperties'); } + foreach (self::$labelProperties as $p) { if (isset($this->_index[$uri][$p])) { return $this->_index[$uri][$p][0]['value']; @@ -1667,21 +1629,18 @@ public function get_label_for_uri($uri) return ''; } - /** - * @return bool - */ - public function is_equal_to(ExtendedGraph $otherGraph) + public function is_equal_to(ExtendedGraph $otherGraph): bool { $diffThisAndThat = $this->diff($this->get_index(), $otherGraph->get_index()); $diffThatAndThis = $this->diff($otherGraph->get_index(), $this->get_index()); - return empty($diffThisAndThat) && empty($diffThatAndThis); + return $diffThisAndThat === [] && $diffThatAndThis === []; } /** - * @param string $type + * @param ObjectResource $type */ - public function remove_subjects_of_type($type) + public function remove_subjects_of_type(string $type): void { $subjects = $this->get_subjects_of_type($type); foreach ($subjects as $s) { @@ -1689,12 +1648,10 @@ public function remove_subjects_of_type($type) } } - public function from_graph(ExtendedGraph $graph) + public function from_graph(ExtendedGraph $graph): void { - if ($graph) { - $this->remove_all_triples(); - $this->add_graph($graph); - } + $this->remove_all_triples(); + $this->add_graph($graph); } /** @@ -1704,44 +1661,31 @@ public function from_graph(ExtendedGraph $graph) * but accepting scalars so we can handle legacy data * which was not type-checked. * - * @param string $value - * - * @return bool + * @param mixed $value */ - protected function isValidLiteral($value) + protected function isValidLiteral($value): bool { - if (!is_scalar($value)) { - return false; - } - - return true; + return is_scalar($value); } /** * Check if a triple value is valid. * - * @param string $value - * - * @return bool + * @param mixed $value */ - protected function isValidResource($value) + protected function isValidResource($value): bool { - if (!is_string($value) || empty($value)) { - return false; - } - - return true; + return is_string($value) && ($value !== '' && $value !== '0'); } /** - * @param string $s - * @param string $p - * - * @return bool + * @param TripleSubject $s + * @param TriplePredicate $p + * @param TripleObject $o_info * * @throws Exception */ - private function _add_triple($s, $p, array $o_info) + private function _add_triple(string $s, string $p, array $o_info): bool { // The value $o should already have been validated by this point // It's validation differs depending on whether it is a literal or resource @@ -1749,20 +1693,24 @@ private function _add_triple($s, $p, array $o_info) if (!$this->isValidResource($s)) { throw new Exception('The subject is invalid'); } + if (!$this->isValidResource($p)) { throw new Exception('The predicate is invalid'); } + if (!isset($this->_index[$s])) { $this->_index[$s] = []; $this->_index[$s][$p] = [$o_info]; return true; } + if (!isset($this->_index[$s][$p])) { $this->_index[$s][$p] = [$o_info]; return true; } + if (!in_array($o_info, $this->_index[$s][$p])) { $this->_index[$s][$p][] = $o_info; @@ -1772,15 +1720,12 @@ private function _add_triple($s, $p, array $o_info) return false; } - /** - * @param array $triples - */ - private function _add_arc2_triple_list(&$triples) + private function _add_arc2_triple_list(array &$triples): void { $bnode_index = []; // We can safely preserve bnode labels if the graph is empty, otherwise we need to rewrite them - $rewrite_bnode_labels = $this->is_empty() ? false : true; + $rewrite_bnode_labels = !$this->is_empty(); foreach ($triples as $t) { $obj = []; @@ -1789,6 +1734,7 @@ private function _add_arc2_triple_list(&$triples) if (!array_key_exists($t['o'], $bnode_index)) { $bnode_index[$t['o']] = uniqid('_:mor'); } + $obj['value'] = $bnode_index[$t['o']]; } else { $obj['value'] = $t['o']; @@ -1798,16 +1744,13 @@ private function _add_arc2_triple_list(&$triples) if (!array_key_exists($t['s'], $bnode_index)) { $bnode_index[$t['s']] = uniqid('_:mor'); } + $t['s'] = $bnode_index[$t['s']]; } if ($t['o_type'] === 'iri') { $obj['type'] = 'uri'; - } elseif ($t['o_type'] === 'literal1' - || $t['o_type'] === 'literal2' - || $t['o_type'] === 'long_literal1' - || $t['o_type'] === 'long_literal2' - ) { + } elseif (in_array($t['o_type'], ['literal1', 'literal2', 'long_literal1', 'long_literal2'], true)) { $obj['type'] = 'literal'; } else { $obj['type'] = $t['o_type']; @@ -1819,6 +1762,7 @@ private function _add_arc2_triple_list(&$triples) } elseif (isset($t['o_datatype']) && $t['o_datatype']) { $obj['datatype'] = $t['o_datatype']; } + if (isset($t['o_lang']) && $t['o_lang']) { $obj['lang'] = $t['o_lang']; } @@ -1829,21 +1773,16 @@ private function _add_arc2_triple_list(&$triples) $this->_index[$t['s']][$t['p']] = [$obj]; } elseif (!isset($this->_index[$t['s']][$t['p']])) { $this->_index[$t['s']][$t['p']] = [$obj]; - } else { - if (!in_array($obj, $this->_index[$t['s']][$t['p']])) { - $this->_index[$t['s']][$t['p']][] = $obj; - } + } elseif (!in_array($obj, $this->_index[$t['s']][$t['p']])) { + $this->_index[$t['s']][$t['p']][] = $obj; } } } - // until ARC2 upgrades to support RDF/PHP we need to rename all types of "uri" to "iri" /** - * @param array $index - * - * @return array + * @param TripleGraph $index */ - private function _to_arc_index(&$index) + private function _to_arc_index(array $index): array { $ret = []; @@ -1854,12 +1793,15 @@ private function _to_arc_index(&$index) foreach ($p_info as $o) { $o_new = []; foreach ($o as $key => $value) { + // until ARC2 upgrades to support RDF/PHP we + // need to rename all types of "uri" to "iri" if ($key == 'type' && $value == 'uri') { $o_new['type'] = 'iri'; } else { $o_new[$key] = $value; } } + $ret[$s][$p][] = $o_new; } } @@ -1869,17 +1811,15 @@ private function _to_arc_index(&$index) } /** - * @param string $p - * @param string $o - * @param string $type - * - * @return array + * @param TriplePredicate $p + * @param ObjectValue $o + * @param ObjectType $type */ - private function get_subjects_where($p, $o, $type) + private function get_subjects_where(string $p, $o, string $type): array { $subjects = []; foreach ($this->_index as $subject => $properties) { - if (array_key_exists($p, $properties)) { + if (isset($properties[$p])) { foreach ($properties[$p] as $object) { if ($object['type'] == $type && $object['value'] == $o) { $subjects[] = $subject; @@ -1894,19 +1834,19 @@ private function get_subjects_where($p, $o, $type) } /** - * @param string $s - * @param string $o - * @param string $type + * @param TripleSubject $s + * @param ObjectValue $o + * @param 'literal'|'resource' $type */ - private function add_to_sequence($s, $o, $type = 'resource') + private function add_to_sequence(string $s, $o, string $type = 'resource'): void { $sequenceValue = $this->get_next_sequence($s); $this->add_resource_triple($s, self::rdf_type, self::rdf_seq); - if ($type == 'literal') { - $this->add_literal_triple($s, self::rdf . "_{$sequenceValue}", $o); + if ($type === 'literal') { + $this->add_literal_triple($s, self::rdf . ('_' . $sequenceValue), $o); } else { - $this->add_resource_triple($s, self::rdf . "_{$sequenceValue}", $o); + $this->add_resource_triple($s, self::rdf . ('_' . $sequenceValue), $o); } } } diff --git a/src/classes/Labeller.php b/src/classes/Labeller.php index 14883cd6..8ee4e678 100644 --- a/src/classes/Labeller.php +++ b/src/classes/Labeller.php @@ -1,5 +1,7 @@ > */ - public $_labels = [ + public array $_labels = [ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_1' => ['first', 'first', 'is first member of'], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_2' => ['second', 'second', 'is second member of'], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_3' => ['third', 'third', 'is third member of'], @@ -25,7 +27,7 @@ class Labeller 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_9' => ['ninth', 'ninth', 'is ninth member of'], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_10' => ['tenth', 'tenth', 'is tenth member of'], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_11' => ['eleventh', 'eleventh', 'is eleventh member of'], - 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_12' => ['twelth', 'twelth', 'is twelth member of'], + 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_12' => ['twelfth', 'twelfth', 'is twelfth member of'], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_13' => ['thirteenth', 'thirteenth', 'is thirteenth member of'], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_14' => ['fourteenth', 'fourteenth', 'is fourteenth member of'], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_15' => ['fifteenth', 'fifteenth', 'is fifteenth member of'], @@ -51,8 +53,8 @@ class Labeller 'http://xmlns.com/foaf/0.1/nick' => ['nickname', 'nickname', 'is nickname of'], 'http://xmlns.com/foaf/0.1/phone' => ['phone number'], 'http://xmlns.com/foaf/0.1/mbox' => ['email address'], - 'http://xmlns.com/foaf/0.1/workplaceHomepage' => ['workplace\'s homepage'], - 'http://xmlns.com/foaf/0.1/schoolHomepage' => ['school\'s homepage'], + 'http://xmlns.com/foaf/0.1/workplaceHomepage' => ["workplace's homepage"], + 'http://xmlns.com/foaf/0.1/schoolHomepage' => ["school's homepage"], 'http://xmlns.com/foaf/0.1/openid' => ['OpenID'], 'http://xmlns.com/foaf/0.1/mbox_sha1sum' => ['email address hashcode'], 'http://xmlns.com/foaf/0.1/made' => ['made', 'made', 'maker'], @@ -237,14 +239,14 @@ class Labeller ]; /** - * @var array + * @var string[] */ - protected $_label_properties = []; + protected array $_label_properties = []; /** - * @var array + * @var array */ - protected $_ns = [ + protected array $_ns = [ 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', 'owl' => 'http://www.w3.org/2002/07/owl#', @@ -294,7 +296,7 @@ class Labeller * @param string $prefix the namespace prefix to associate with the URI * @param string $uri the URI to associate with the prefix */ - public function set_namespace_mapping($prefix, $uri) + public function set_namespace_mapping(string $prefix, string $uri): void { $this->_ns[$prefix] = $uri; } @@ -302,16 +304,18 @@ public function set_namespace_mapping($prefix, $uri) /** * Convert a QName to a URI using registered namespace prefixes. * - * @param string $qname the QName to convert + * @param string|null $qName the QName to convert * - * @return string the URI corresponding to the QName if a suitable prefix exists, null otherwise + * @return string|null the URI corresponding to the QName if a suitable prefix exists, null otherwise */ - public function qname_to_uri($qname) + public function qname_to_uri(?string $qName): ?string { - if (preg_match('~^(.+):(.+)$~', $qname, $m)) { - if (isset($this->_ns[$m[1]])) { - return $this->_ns[$m[1]] . $m[2]; - } + if ($qName === null || !preg_match('~^(.+):(.+)$~', $qName, $m)) { + return null; + } + + if (isset($this->_ns[$m[1]])) { + return $this->_ns[$m[1]] . $m[2]; } return null; @@ -320,17 +324,17 @@ public function qname_to_uri($qname) /** * Convert a URI to a QName using registered namespace prefixes. * - * @param string $uri the URI to convert + * @param string|null $uri the URI to convert * - * @return string the QName corresponding to the URI if a suitable prefix exists, null otherwise + * @return string|null the QName corresponding to the URI if a suitable prefix exists, null otherwise */ - public function uri_to_qname($uri) + public function uri_to_qname(?string $uri): ?string { - if (preg_match('~^(.*[\/\#])([a-z0-9\-\_\:]+)$~i', $uri, $m)) { + if ($uri !== null && preg_match('~^(.*[\/\#])([a-z0-9\-\_\:]+)$~i', $uri, $m)) { $ns = $m[1]; $localname = $m[2]; $prefix = $this->get_prefix($ns); - if ($prefix != null && $prefix !== false) { + if ($prefix) { return $prefix . ':' . $localname; } } @@ -338,21 +342,27 @@ public function uri_to_qname($uri) return null; } - /** - * @param string $ns - * - * @return string - */ - public function get_prefix($ns) + public function get_prefix(string $ns): string { - $prefix = array_search($ns, $this->_ns); - if ($prefix != null && $prefix !== false) { + $prefix = array_search($ns, $this->_ns, true); + if ($prefix) { return $prefix; } + /** @var non-empty-list $parts */ $parts = preg_split('/[\/#]/', $ns); for ($i = count($parts) - 1; $i >= 0; $i--) { - if (preg_match('~^[a-zA-Z][a-zA-Z0-9\-]+$~', $parts[$i]) && !array_key_exists($parts[$i], $this->_ns) && $parts[$i] != 'schema' && $parts[$i] != 'ontology' && $parts[$i] != 'vocab' && $parts[$i] != 'terms' && $parts[$i] != 'ns' && $parts[$i] != 'core' && strlen($parts[$i]) > 3) { + if ( + preg_match('~^[a-zA-Z][a-zA-Z0-9\-]+$~', $parts[$i]) + && !isset($this->_ns[$parts[$i]]) + && $parts[$i] != 'schema' + && $parts[$i] != 'ontology' + && $parts[$i] != 'vocab' + && $parts[$i] != 'terms' + && $parts[$i] != 'ns' + && $parts[$i] != 'core' + && strlen($parts[$i]) > 3 + ) { $prefix = strtolower($parts[$i]); $this->_ns[$prefix] = $ns; @@ -361,97 +371,85 @@ public function get_prefix($ns) } $index = 0; - while (array_key_exists('ns' . $index, $this->_ns)) { + while (isset($this->_ns['ns' . $index])) { $index++; } + $prefix = 'msg' . $index; $this->_ns[$prefix] = $ns; return $prefix; } - /** - * @param string $p - */ - public function add_labelling_property($p) + public function add_labelling_property(string $p): void { $this->_label_properties[] = $p; } - /** - * @return array - */ - public function get_ns() + public function get_ns(): array { return $this->_ns; } - /** - * @param string $uri - * @param ExtendedGraph|null $g - * @param bool $capitalize - * @param bool $use_qnames - * - * @return string - */ - public function get_label($uri, $g = null, $capitalize = false, $use_qnames = false) + public function get_label(string $uri, ?ExtendedGraph $g = null, bool $capitalize = false, bool $use_qnames = false): string { - if ($g) { + if ($g instanceof ExtendedGraph) { $label = $g->get_first_literal($uri, 'http://www.w3.org/2004/02/skos/core#prefLabel', '', 'en'); - if (strlen($label) != 0) { + if ($label) { return $label; } $label = $g->get_first_literal($uri, RDFS_LABEL, '', 'en'); - if (strlen($label) != 0) { + if ($label) { return $label; } $label = $g->get_first_literal($uri, 'http://purl.org/dc/terms/title', '', 'en'); - if (strlen($label) != 0) { + if ($label) { return $label; } $label = $g->get_first_literal($uri, DC_TITLE, '', 'en'); - if (strlen($label) != 0) { + if ($label) { return $label; } $label = $g->get_first_literal($uri, FOAF_NAME, '', 'en'); - if (strlen($label) != 0) { + if ($label) { return $label; } $label = $g->get_first_literal($uri, 'http://www.geonames.org/ontology#name', '', 'en'); - if (strlen($label) != 0) { + if ($label) { return $label; } $label = $g->get_first_literal($uri, RDF_VALUE, '', 'en'); - if (strlen($label) != 0) { + if ($label) { return $label; } $label = $g->get_first_literal($uri, 'http://purl.org/rss/1.0/title', '', 'en'); - if (strlen($label) != 0) { + if ($label) { return $label; } foreach ($this->_label_properties as $p) { $label = $g->get_first_literal($uri, $p, '', 'en'); - if (strlen($label) != 0) { + if ($label) { return $label; } } } - if (array_key_exists($uri, $this->_labels)) { + if (isset($this->_labels[$uri])) { if ($capitalize) { return ucfirst($this->_labels[$uri][0]); } return $this->_labels[$uri][0]; } + if (preg_match('~^http://www.w3.org/1999/02/22-rdf-syntax-ns#_(.+)$~', $uri, $m)) { if ($capitalize) { return 'Item ' . $m[1]; @@ -465,64 +463,54 @@ public function get_label($uri, $g = null, $capitalize = false, $use_qnames = fa if ($label) { return $label; } - } else { - if (preg_match('~^.*[\/\#]([^\/\#]+)$~', $uri, $m)) { - $localname = $m[1]; - if (preg_match('~[^A-Z][A-Z][^A-Z]~', $localname)) { - $parts = preg_split('/([A-Z][^A-Z]*)/', $localname, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); - $parts = array_map('strtolower', $parts); - if ($parts[0] == 'has') { - array_shift($parts); - } - $label = implode(' ', $parts); - if ($capitalize) { - return ucfirst($label); - } - - return $label; + } elseif (preg_match('~^.*[\/\#]([^\/\#]+)$~', $uri, $m)) { + $localname = $m[1]; + if (preg_match('~[^A-Z][A-Z][^A-Z]~', $localname)) { + /** @var non-empty-list $parts */ + $parts = preg_split('/([A-Z][^A-Z]*)/', $localname, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); + $parts = array_map('strtolower', $parts); + if ($parts[0] == 'has') { + array_shift($parts); } - if ($capitalize && preg_match('~^[a-z]~', $localname)) { - return ucfirst($localname); + $label = implode(' ', $parts); + if ($capitalize) { + return ucfirst($label); } - return $localname; + return $label; + } + + if ($capitalize && preg_match('~^[a-z]~', $localname)) { + return ucfirst($localname); } + + return $localname; } return $uri; } - /** - * @param string $uri - * @param ExtendedGraph|null $g - * @param bool $capitalize - * @param bool $use_qnames - * - * @return string - */ - public function get_plural_label($uri, $g = null, $capitalize = false, $use_qnames = false) + public function get_plural_label(string $uri, ?ExtendedGraph $g = null, bool $capitalize = false, bool $use_qnames = false): string { - if ($g) { + if ($g instanceof ExtendedGraph) { $label = $g->get_first_literal($uri, 'http://purl.org/net/vocab/2004/03/label#plural', '', 'en'); - if (strlen($label) != 0) { + if ($label) { return $label; } } - if (array_key_exists($uri, $this->_labels)) { - if (count($this->_labels[$uri]) > 1) { - $label = $this->_labels[$uri][1]; - } else { - $label = $this->_labels[$uri][0] . 's'; - } + if (isset($this->_labels[$uri])) { + $label = count($this->_labels[$uri]) > 1 ? $this->_labels[$uri][1] : $this->_labels[$uri][0] . 's'; + if ($capitalize) { return ucfirst($label); } return $label; } - if ($use_qnames == false && preg_match('~^.*[\/\#]([a-z]+)$~', $uri, $m)) { + + if ($use_qnames === false && preg_match('~^.*[\/\#]([a-z]+)$~', $uri, $m)) { return $m[1] . 's'; } @@ -534,29 +522,18 @@ public function get_plural_label($uri, $g = null, $capitalize = false, $use_qnam return $label; } - /** - * @param string $uri - * @param ExtendedGraph|null $g - * @param bool $capitalize - * @param bool $use_qnames - * - * @return string - */ - public function get_inverse_label($uri, $g = null, $capitalize = false, $use_qnames = false) + public function get_inverse_label(string $uri, ?ExtendedGraph $g = null, bool $capitalize = false, bool $use_qnames = false): string { - if ($g) { + if ($g instanceof ExtendedGraph) { $label = $g->get_first_literal($uri, 'http://purl.org/net/vocab/2004/03/label#inverseSingular', '', 'en'); - if (strlen($label) != 0) { + if ($label) { return $label; } } - if (array_key_exists($uri, $this->_labels)) { - if (count($this->_labels[$uri]) > 2) { - $label = $this->_labels[$uri][2]; - } else { - $label = 'is ' . $this->_labels[$uri][0] . ' of'; - } + if (isset($this->_labels[$uri])) { + $label = count($this->_labels[$uri]) > 2 ? $this->_labels[$uri][2] : 'is ' . $this->_labels[$uri][0] . ' of'; + if ($capitalize) { return ucfirst($label); } @@ -572,17 +549,18 @@ public function get_inverse_label($uri, $g = null, $capitalize = false, $use_qna return $label; } - public function label_graph(ExtendedGraph &$graph) + public function label_graph(ExtendedGraph &$graph): void { $labelled_properties = []; $index = $graph->get_index(); foreach ($index as $p_list) { foreach ($p_list as $p => $val) { if (!array_key_exists($p, $labelled_properties)) { - if (array_key_exists($p, $this->_labels)) { + if (isset($this->_labels[$p])) { if (!$graph->subject_has_property($p, RDFS_LABEL)) { $graph->add_literal_triple($p, RDFS_LABEL, $this->_labels[$p][0]); } + if (!$graph->subject_has_property($p, 'http://purl.org/net/vocab/2004/03/label#plural')) { if (count($this->_labels[$p]) > 1) { $graph->add_literal_triple($p, 'http://purl.org/net/vocab/2004/03/label#plural', $this->_labels[$p][1]); @@ -590,6 +568,7 @@ public function label_graph(ExtendedGraph &$graph) $graph->add_literal_triple($p, 'http://purl.org/net/vocab/2004/03/label#plural', $this->_labels[$p][0] . 's'); } } + if (!$graph->subject_has_property($p, 'http://purl.org/net/vocab/2004/03/label#inverseSingular')) { if (count($this->_labels[$p]) > 2) { $graph->add_literal_triple($p, 'http://purl.org/net/vocab/2004/03/label#inverseSingular', $this->_labels[$p][2]); @@ -597,6 +576,7 @@ public function label_graph(ExtendedGraph &$graph) $graph->add_literal_triple($p, 'http://purl.org/net/vocab/2004/03/label#inverseSingular', 'is ' . $this->_labels[$p][0] . ' of'); } } + $labelled_properties[$p] = 1; } elseif (preg_match('~^http://www.w3.org/1999/02/22-rdf-syntax-ns#_(.+)$~', $p, $m)) { $graph->add_literal_triple($p, RDFS_LABEL, 'Item ' . $m[1]); diff --git a/src/classes/NoStat.php b/src/classes/NoStat.php new file mode 100644 index 00000000..435bde20 --- /dev/null +++ b/src/classes/NoStat.php @@ -0,0 +1,48 @@ +host = $host; - $this->port = $port; + $this->setHost($host); + $this->setPort($port); $this->setPrefix($prefix); } - /** - * @param string $operation - * @param int $inc - */ - public function increment($operation, $inc = 1) + public function increment(string $operation, int $inc = 1): void { $this->send( $this->generateStatData($operation, $inc . '|c') @@ -40,25 +32,19 @@ public function increment($operation, $inc = 1) } /** - * @param string $operation - * @param number $duration - * - * @return mixed + * @param float|int $duration */ - public function timer($operation, $duration) + public function timer(string $operation, $duration): void { $this->send( - $this->generateStatData($operation, ['1|c', "{$duration}|ms"]) + $this->generateStatData($operation, ['1|c', $duration . '|ms']) ); } /** * Record an arbitrary value. - * - * @param string $operation - * @param string $value */ - public function gauge($operation, $value) + public function gauge(string $operation, string $value): void { $this->send( $this->generateStatData($operation, $value . '|g') @@ -66,9 +52,9 @@ public function gauge($operation, $value) } /** - * @return array + * @return array|class-string> */ - public function getConfig() + public function getConfig(): array { return [ 'class' => get_class($this), @@ -81,7 +67,7 @@ public function getConfig() } /** - * @return StatsD + * @return self */ public static function createFromConfig(array $config) { @@ -96,20 +82,15 @@ public static function createFromConfig(array $config) return new self($host, $port, $prefix); } - /** - * @return string - */ - public function getPrefix() + public function getPrefix(): ?string { return $this->prefix; } /** - * @param string $prefix - * * @throws \InvalidArgumentException */ - public function setPrefix($prefix) + public function setPrefix(?string $prefix): void { if ($this->isValidPathValue($prefix)) { $this->prefix = $prefix; @@ -118,10 +99,7 @@ public function setPrefix($prefix) } } - /** - * @return int|string - */ - public function getPort() + public function getPort(): int { return $this->port; } @@ -129,41 +107,30 @@ public function getPort() /** * @param int|string $port */ - public function setPort($port) + public function setPort($port): void { - $this->port = $port; + $this->port = (int) $port; } - /** - * @return string - */ - public function getHost() + public function getHost(): string { return $this->host; } - /** - * @param string $host - */ - public function setHost($host) + public function setHost(string $host): void { $this->host = $host; } - /** - * @return string - */ - public function getPivotValue() + public function getPivotValue(): ?string { return $this->pivotValue; } /** - * @param string $pivotValue - * * @throws \InvalidArgumentException */ - public function setPivotValue($pivotValue) + public function setPivotValue(?string $pivotValue): void { if ($this->isValidPathValue($pivotValue)) { $this->pivotValue = $pivotValue; @@ -174,45 +141,47 @@ public function setPivotValue($pivotValue) /** * Sends the stat(s) using UDP protocol. - * - * @param array $data - * @param int $sampleRate */ - protected function send($data, $sampleRate = 1) + protected function send(array $data, int $sampleRate = 1): void { + if (empty($this->host)) { + return; + } + $sampledData = []; if ($sampleRate < 1) { foreach ($data as $stat => $value) { if ((mt_rand() / mt_getrandmax()) <= $sampleRate) { - $sampledData[$stat] = "{$value}|@{$sampleRate}"; + $sampledData[$stat] = sprintf('%s|@%d', $value, $sampleRate); } } } else { $sampledData = $data; } + if (empty($sampledData)) { return; } try { - if (!empty($this->host)) { // if host is configured, send.. - $fp = fsockopen("udp://{$this->host}", $this->port); - if (!$fp) { - return; - } - // make this a non blocking stream - stream_set_blocking($fp, false); - foreach ($sampledData as $stat => $value) { - if (is_array($value)) { - foreach ($value as $v) { - fwrite($fp, "{$stat}:{$v}"); - } - } else { - fwrite($fp, "{$stat}:{$value}"); + $fp = fsockopen('udp://' . $this->host, $this->port); + if (!$fp) { + return; + } + + // make this a non blocking stream + stream_set_blocking($fp, false); + foreach ($sampledData as $stat => $value) { + if (is_array($value)) { + foreach ($value as $v) { + fwrite($fp, sprintf('%s:%s', $stat, $v)); } + } else { + fwrite($fp, sprintf('%s:%s', $stat, $value)); } - fclose($fp); } + + fclose($fp); } catch (\Exception $e) { } } @@ -224,46 +193,35 @@ protected function send($data, $sampleRate = 1) * "{prefix}.tripod.{stat}"=>"1|c" * } * - * @param string $operation * @param array|string $value * * @return array An associative array of the grouped_by_database and aggregate stats */ - protected function generateStatData($operation, $value) + protected function generateStatData(string $operation, $value): array { $data = []; foreach ($this->getStatsPaths() as $path) { - $data[$path . ".{$operation}"] = $value; + $data[$path . ('.' . $operation)] = $value; } return $data; } - /** - * @return array - */ - protected function getStatsPaths() + protected function getStatsPaths(): array { - return array_values(array_filter([$this->getAggregateStatPath()])); + return array_filter([$this->getAggregateStatPath()]); } - /** - * @return string - */ - protected function getAggregateStatPath() + protected function getAggregateStatPath(): string { return empty($this->prefix) ? STAT_CLASS : $this->prefix . '.' . STAT_CLASS; } /** * StatsD paths cannot start with, end with, or have more than one consecutive '.'. - * - * @param string $value - * - * @return bool */ - protected function isValidPathValue($value) + protected function isValidPathValue(?string $value): bool { - return preg_match('/(^\.)|(\.\.+)|(\.$)/', $value) === 0; + return $value === null || preg_match('/(^\.)|(\.\.+)|(\.$)/', $value) === 0; } } diff --git a/src/classes/Timer.php b/src/classes/Timer.php index 53149904..9a715c1e 100644 --- a/src/classes/Timer.php +++ b/src/classes/Timer.php @@ -1,5 +1,7 @@ start_time = $this->getMicrotime(); } @@ -37,7 +39,7 @@ public function start() /** * Captures current microtime as end time. Call this as soon as event execution is complete. */ - public function stop() + public function stop(): void { $this->end_time = $this->getMicrotime(); } @@ -45,15 +47,16 @@ public function stop() /** * Calculate difference between start and end time of event and return in milli-seconds. * - * @return number time difference in milliseconds between start and end time of event + * @return int time difference in milliseconds between start and end time of event * - * @throws \Exception + * @throws \Exception If either of or both of start or stop method are not called before this method */ - public function result() + public function result(): int { if (is_null($this->start_time)) { throw new \Exception('Timer: start method not called !'); } + if (is_null($this->end_time)) { throw new \Exception('Timer: stop method not called !'); } @@ -64,7 +67,7 @@ public function result() $differenceInMilliSeconds = ((float) $endTimeSeconds - (float) $startTimeSeconds) * 1000; - $this->result = round(($differenceInMilliSeconds + ((float) $endTimeMicroSeconds * 1000)) - (float) $startTimeMicroSeconds * 1000); + $this->result = (int) round(($differenceInMilliSeconds + ((float) $endTimeMicroSeconds * 1000)) - (float) $startTimeMicroSeconds * 1000); } return $this->result; @@ -73,15 +76,16 @@ public function result() /** * Calculate difference between start and end time of event and return in micro-seconds. * - * @return number time difference in micro seconds between start and end time of event + * @return int time difference in micro seconds between start and end time of event * - * @throws \Exception, if either of or both of start or stop method are not called before this method + * @throws \Exception If either of or both of start or stop method are not called before this method */ - public function microResult() + public function microResult(): int { if (is_null($this->start_time)) { throw new TimerException('Timer: start method not called !'); } + if (is_null($this->end_time)) { throw new TimerException('Timer: stop method not called !'); } @@ -92,7 +96,7 @@ public function microResult() $differenceInMicroSeconds = ((float) $endTimeSeconds - (float) $startTimeSeconds) * 1000000; - $this->micro_result = round(($differenceInMicroSeconds + ((float) $endTimeMicroSeconds * 1000000)) - (float) $startTimeMicroSeconds * 1000000); + $this->micro_result = (int) round(($differenceInMicroSeconds + ((float) $endTimeMicroSeconds * 1000000)) - (float) $startTimeMicroSeconds * 1000000); } return $this->micro_result; @@ -101,7 +105,7 @@ public function microResult() /** * @return string current system time in pair of seconds microseconds */ - private function getMicrotime() + private function getMicrotime(): string { return microtime(); } diff --git a/src/exceptions/CardinalityException.php b/src/exceptions/CardinalityException.php index 99eecbca..1b939e5a 100644 --- a/src/exceptions/CardinalityException.php +++ b/src/exceptions/CardinalityException.php @@ -1,5 +1,7 @@ target = $target; - parent::__construct("Could not label: {$target}"); + parent::__construct('Could not label: ' . $target); } - /** - * @return string - */ - public function getTarget() + public function getTarget(): ?string { return $this->target; } diff --git a/src/exceptions/SearchException.php b/src/exceptions/SearchException.php index 1f22738d..5accc8bf 100644 --- a/src/exceptions/SearchException.php +++ b/src/exceptions/SearchException.php @@ -1,5 +1,7 @@ An associative array of namespaces, keyed by prefix */ - protected $ns = []; + protected array $ns = []; /** * The transaction log db config. - * - * @var array */ - protected $tConfig = []; + protected array $tConfig = []; /** - * The value should be the name of a class that implement iTripodSearchProvider keyed by storename. + * The value should be the name of a class that implement ISearchProvider keyed by storename. * - * @var array + * @var array> */ - protected $searchProviderClassName = []; + protected array $searchProviderClassName = []; /** * All of the predicates associated with a particular spec document. - * - * @var array */ - protected $specPredicates; + protected array $specPredicates; /** * A simple map between collection names and the database name they belong to. - * - * @var array */ - protected $collectionDatabases = []; + protected array $collectionDatabases = []; - /** - * @var array - */ - protected $activeMongoConnections = []; + protected array $activeMongoConnections = []; - /** - * @var string - */ - protected $defaultDatabase; + protected array $dataSources = []; - /** - * @var array - */ - protected $dataSources = []; + protected array $podConnections = []; - /** - * @var array - */ - protected $podConnections = []; - - /** - * @var string - */ - protected static $validationLevel = self::VALIDATE_MIN; + protected static string $validationLevel = self::VALIDATE_MIN; /** * Database connections, keyed by datasource, so we're not inadvertently opening many db connections through getDatabase(). - * - * @var array */ - protected $connections = []; + protected array $connections = []; + + protected int $mongoCursorTimeout = 30000; /** - * @var int + * @var array */ - protected $mongoCursorTimeout = 30000; - - protected $batchSize = [ + protected array $batchSize = [ OP_TABLES => 100, OP_SEARCH => 100, OP_VIEWS => 25, ]; - /** - * @var LoggerInterface - */ - protected static $logger; + protected static ?LoggerInterface $logger = null; - /** - * @var array - */ - private $config; + private ?array $config = null; - /** - * @var Labeller - */ - private $labeller; + private ?Labeller $labeller = null; - /** - * @var string - */ - private $defaultContext; + private string $defaultContext; /** * The defined database indexes, keyed by database name. - * - * @var array */ - private $indexes = []; + private array $indexes = []; - /** - * @var array - */ - private $cardinality = []; + private array $cardinality = []; /** * The connection strings for each defined database. - * - * @var array */ - private $dbConfig = []; + private array $dbConfig = []; /** * All of the defined viewSpecs. - * - * @var array */ - private $viewSpecs = []; + private array $viewSpecs = []; /** * All of the defined tableSpecs. - * - * @var array */ - private $tableSpecs = []; + private array $tableSpecs = []; /** * Defined database configuration: dbname, collections, etc. - * - * @var array */ - private $databases = []; + private array $databases = []; /** * Config should not be instantiated directly: use Config::getInstance(). */ private function __construct() {} - /** - * @return int - */ - public function getMongoCursorTimeout() + public function getMongoCursorTimeout(): int { return $this->mongoCursorTimeout; } - /** - * @param int $mongoCursorTimeout - */ - public function setMongoCursorTimeout($mongoCursorTimeout) + public function setMongoCursorTimeout(int $mongoCursorTimeout): void { $this->mongoCursorTimeout = $mongoCursorTimeout; } /** + * @param array $spec + * * @throws ConfigException */ - public function validateTableSpec(array $spec) + public function validateTableSpec(array $spec): void { if (!isset($spec[_ID_KEY])) { throw new ConfigException('Table spec does not contain ' . _ID_KEY); @@ -203,18 +152,12 @@ public function validateTableSpec(array $spec) $this->validateTableSpecPart($spec, 0); } - /** - * @return string - */ - public function getValidationLevel() + public function getValidationLevel(): string { return self::$validationLevel; } - /** - * @param string $validationLevel - */ - public static function setValidationLevel($validationLevel) + public static function setValidationLevel(string $validationLevel): void { self::$validationLevel = $validationLevel; } @@ -222,13 +165,8 @@ public static function setValidationLevel($validationLevel) /** * Returns an array of associated predicates in a table or search document specification * Note: will not return viewSpec predicates. - * - * @param string $storename - * @param string $specId - * - * @return array */ - public function getDefinedPredicatesInSpec($storename, $specId) + public function getDefinedPredicatesInSpec(string $storename, string $specId): array { if (!isset($this->specPredicates[$storename])) { $this->specPredicates[$storename] = $this->getDefinedPredicatesInSpecs($storename); @@ -240,12 +178,11 @@ public function getDefinedPredicatesInSpec($storename, $specId) /** * Check modifier functions against fields. * - * @param array|bool $parent - * @param string|null $parentKey + * @param array|bool $parent * * @throws ConfigException */ - public function checkModifierFunctions(array $array, $parent, $parentKey = null) + public function checkModifierFunctions(array $array, $parent, ?string $parentKey = null): void { foreach ($array as $k => $v) { // You can have recursive modifiers so we check if the value is an array. @@ -253,19 +190,19 @@ public function checkModifierFunctions(array $array, $parent, $parentKey = null) // Check config // Valid configs can be top level modifiers and their attributes inside - you can have a top level modifier // inside a top level modifier - that's why we also check \Tripod\Mongo\Composites\Tables::$predicatesModifiers direct - if (!array_key_exists($k, $parent) && !array_key_exists($k, Tables::$predicateModifiers)) { + if (!isset($parent[$k]) && !isset(Tables::$predicateModifiers[$k])) { throw new ConfigException("Invalid modifier: '" . $k . "' in key '" . $parentKey . "'"); } // If this config value is a top level modifier, use that as the parent so that we can check the attributes - if (array_key_exists($k, Tables::$predicateModifiers)) { + if (isset(Tables::$predicateModifiers[$k])) { $this->checkModifierFunctions($v, Tables::$predicateModifiers[$k], $k); } else { $this->checkModifierFunctions($v, $parent[$k], $k); } } elseif (is_string($k)) { // Check key - if (!array_key_exists($k, $parent)) { + if (!isset($parent[$k])) { throw new ConfigException("Invalid modifier: '" . $k . "' in key '" . $parentKey . "'"); } } @@ -274,10 +211,8 @@ public function checkModifierFunctions(array $array, $parent, $parentKey = null) /** * Returns an alias curie of the default context (i.e. graph name). - * - * @return string */ - public function getDefaultContextAlias() + public function getDefaultContextAlias(): string { return $this->getLabeller()->uri_to_alias($this->defaultContext); } @@ -289,14 +224,12 @@ public function getDefaultContextAlias() * * @deprecated * - * @return Config - * * @throws ConfigException */ - public static function getInstance() + public static function getInstance(): IConfigInstance { self::getLogger()->warning( - '\Tripod\Mongo\Config::getInstance deprecated, use \Tripod\Config::getInstance instead' + Config::class . '::getInstance deprecated, use ' . \Tripod\Config::class . '::getInstance instead' ); return \Tripod\Config::getInstance(); @@ -307,10 +240,10 @@ public static function getInstance() * * @deprecated */ - public static function setConfig(array $config) + public static function setConfig(array $config): void { self::getLogger()->warning( - '\Tripod\Mongo\Config::setConfig deprecated, use \Tripod\Config::setConfig instead' + Config::class . '::setConfig deprecated, use ' . \Tripod\Config::class . '::setConfig instead' ); \Tripod\Config::setConfig($config); } @@ -319,13 +252,11 @@ public static function setConfig(array $config) * Returns configuration array. * * @deprecated - * - * @return array */ - public static function getConfig() + public static function getConfig(): array { self::getLogger()->warning( - '\Tripod\Mongo\Config::getConfig deprecated, use \Tripod\Config::getConfig instead' + Config::class . '::getConfig deprecated, use ' . \Tripod\Config::class . '::getConfig instead' ); return \Tripod\Config::getConfig(); @@ -333,12 +264,8 @@ public static function getConfig() /** * Returns a list of the configured indexes grouped by collection. - * - * @param string $storeName - * - * @return array */ - public function getIndexesGroupedByCollection($storeName) + public function getIndexesGroupedByCollection(string $storeName): array { $indexes = $this->indexes[$storeName]; // TODO: if we have much more default indexes we should find a better way of doing this @@ -351,30 +278,34 @@ public function getIndexesGroupedByCollection($storeName) // also add the indexes for any views/tables $tableIndexes = []; foreach ($this->getTableSpecifications($storeName) as $tspec) { - if (array_key_exists('ensureIndexes', $tspec)) { + if (isset($tspec['ensureIndexes'])) { // Indexes should be keyed by data_source if (!isset($tableIndexes[$tspec['to_data_source']])) { $tableIndexes[$tspec['to_data_source']] = []; } + foreach ($tspec['ensureIndexes'] as $index) { $tableIndexes[$tspec['to_data_source']][] = $index; } } } + $indexes[TABLE_ROWS_COLLECTION] = $tableIndexes; $viewIndexes = []; foreach ($this->getViewSpecifications($storeName) as $vspec) { - if (array_key_exists('ensureIndexes', $vspec)) { + if (isset($vspec['ensureIndexes'])) { // Indexes should be keyed by data_source if (!isset($viewIndexes[$vspec['to_data_source']])) { $viewIndexes[$vspec['to_data_source']] = []; } + foreach ($vspec['ensureIndexes'] as $index) { $viewIndexes[$vspec['to_data_source']][] = $index; } } } + $indexes[VIEWS_COLLECTION] = $viewIndexes; return $indexes; @@ -390,75 +321,56 @@ public function getIndexesGroupedByCollection($storeName) * @return array|int if no qname is specified then returns an array of cardinality options, * otherwise returns the cardinality value for the given qname */ - public function getCardinality($storeName, $collName, $qName = null) + public function getCardinality(string $storeName, string $collName, ?string $qName = null) { // If no qname specified the return all cardinality rules for this db/collection. if (empty($qName)) { return $this->cardinality[$storeName][$collName]; } - // Return the cardinality rule for the specified qname. - if (array_key_exists($qName, $this->cardinality[$storeName][$collName])) { - return $this->cardinality[$storeName][$collName][$qName]; - } - - return -1; + return $this->cardinality[$storeName][$collName][$qName] ?? -1; } /** * Returns a boolean reflecting whether or not the database and collection are defined in the config. - * - * @param string $storeName - * @param string $pod - * - * @return bool */ - public function isPodWithinStore($storeName, $pod) + public function isPodWithinStore(string $storeName, string $pod): bool { - return (array_key_exists($storeName, $this->podConnections)) ? array_key_exists($pod, $this->podConnections[$storeName]) : false; + return isset($this->podConnections[$storeName][$pod]); } /** * Returns an array of collection configurations for the supplied database name. - * - * @param string $storeName - * - * @return array */ - public function getPods($storeName) + public function getPods(string $storeName): array { - return (array_key_exists($storeName, $this->podConnections)) ? array_keys($this->podConnections[$storeName]) : []; + return isset($this->podConnections[$storeName]) ? array_keys($this->podConnections[$storeName]) : []; } /** * Returns the name of the data source for the request pod. This may be the default for the store or the pod may * have overridden it in the config. * - * @param string $storeName - * @param string $podName - * - * @return string - * * @throws ConfigException */ - public function getDataSourceForPod($storeName, $podName) + public function getDataSourceForPod(string $storeName, string $podName): string { - if (isset($this->podConnections[$storeName], $this->podConnections[$storeName][$podName])) { + if (isset($this->podConnections[$storeName][$podName])) { return $this->podConnections[$storeName][$podName]; } - throw new ConfigException("'{$podName}' not configured for store '{$storeName}'"); + throw new ConfigException(sprintf("'%s' not configured for store '%s'", $podName, $storeName)); } /** * Returns a replica set name for the database, if one has been defined. - * - * @param string $datasource - * - * @return string|null */ - public function getReplicaSetName($datasource) + public function getReplicaSetName(string $datasource): ?string { + if (empty($this->dataSources[$datasource])) { + throw new ConfigException(sprintf("Data source '%s' not in configuration", $datasource)); + } + if (!empty($this->dataSources[$datasource]['replicaSet'])) { return $this->dataSources[$datasource]['replicaSet']; } @@ -467,7 +379,7 @@ public function getReplicaSetName($datasource) $query = parse_url($this->dataSources[$datasource]['connection'], PHP_URL_QUERY); $params = []; parse_str($query, $params); - if (!empty($params['replicaSet'])) { + if (isset($params['replicaSet']) && ($params['replicaSet'] !== [] && ($params['replicaSet'] !== '' && $params['replicaSet'] !== '0'))) { return $params['replicaSet']; } } @@ -477,39 +389,23 @@ public function getReplicaSetName($datasource) /** * Returns a boolean reflecting whether or not a replica set has been defined for the supplied database name. - * - * @param string $datasource - * - * @return bool */ - public function isReplicaSet($datasource) + public function isReplicaSet(string $datasource): bool { return $this->getReplicaSetName($datasource) !== null; } - /** - * @param string $storeName - */ - public function getDefaultDataSourceForStore($storeName) + public function getDefaultDataSourceForStore(string $storeName): ?string { - if (array_key_exists($storeName, $this->dbConfig)) { - return $this->dbConfig[$storeName]['data_source']; - } - - return null; + return $this->dbConfig[$storeName]['data_source'] ?? null; } /** * Return the view specification document for the supplied id, if it exists. - * - * @param string $storeName - * @param string $vid - * - * @return array|null */ - public function getViewSpecification($storeName, $vid) + public function getViewSpecification(string $storeName, string $vid): ?array { - if (isset($this->viewSpecs[$storeName], $this->viewSpecs[$storeName][$vid])) { + if (isset($this->viewSpecs[$storeName][$vid])) { return $this->viewSpecs[$storeName][$vid]; } @@ -518,35 +414,24 @@ public function getViewSpecification($storeName, $vid) /** * Returns the search document specification for the supplied id, if it exists. - * - * @param string $storeName - * @param string $sid - * - * @return array|null */ - public function getSearchDocumentSpecification($storeName, $sid) + public function getSearchDocumentSpecification(string $storeName, string $sid): ?array { - if (array_key_exists($storeName, $this->searchDocSpecs) && array_key_exists($sid, $this->searchDocSpecs[$storeName])) { - return $this->searchDocSpecs[$storeName][$sid]; - } - - return null; + return $this->searchDocSpecs[$storeName][$sid] ?? null; } /** * Returns an array of all search document specifications, or specification ids. * - * @param string $storeName * @param string|null $type When supplied, will only return search document specifications that are triggered by this rdf:type * @param bool $justReturnSpecId default is false. If true will only return an array of specification _id's, otherwise returns the array of specification documents - * - * @return array */ - public function getSearchDocumentSpecifications($storeName, $type = null, $justReturnSpecId = false) + public function getSearchDocumentSpecifications(string $storeName, ?string $type = null, bool $justReturnSpecId = false): array { - if (!isset($this->searchDocSpecs[$storeName]) || empty($this->searchDocSpecs[$storeName])) { + if (empty($this->searchDocSpecs[$storeName])) { return []; } + $specs = []; if (empty($type)) { @@ -569,20 +454,10 @@ public function getSearchDocumentSpecifications($storeName, $type = null, $justR foreach ($this->searchDocSpecs[$storeName] as $spec) { if (is_array($spec[_ID_TYPE])) { if (in_array($typeAsUri, $spec[_ID_TYPE]) || in_array($typeAsQName, $spec[_ID_TYPE])) { - if ($justReturnSpecId) { - $specs[] = $spec[_ID_KEY]; - } else { - $specs[] = $spec; - } - } - } else { - if ($spec[_ID_TYPE] == $typeAsUri || $spec[_ID_TYPE] == $typeAsQName) { - if ($justReturnSpecId) { - $specs[] = $spec[_ID_KEY]; - } else { - $specs[] = $spec; - } + $specs[] = $justReturnSpecId ? $spec[_ID_KEY] : $spec; } + } elseif ($spec[_ID_TYPE] == $typeAsUri || $spec[_ID_TYPE] == $typeAsQName) { + $specs[] = $justReturnSpecId ? $spec[_ID_KEY] : $spec; } } @@ -591,31 +466,18 @@ public function getSearchDocumentSpecifications($storeName, $type = null, $justR /** * Returns the requested table specification, if it exists. - * - * @param string $storeName - * @param string $tid - * - * @return array|null */ - public function getTableSpecification($storeName, $tid) + public function getTableSpecification(string $storeName, string $tid): ?array { - if (isset($this->tableSpecs[$storeName], $this->tableSpecs[$storeName][$tid])) { - return $this->tableSpecs[$storeName][$tid]; - } - - return null; + return $this->tableSpecs[$storeName][$tid] ?? null; } /** * Returns all defined table specifications. * * @codeCoverageIgnore - * - * @param string $storeName - * - * @return array */ - public function getTableSpecifications($storeName) + public function getTableSpecifications(string $storeName): array { return $this->tableSpecs[$storeName] ?? []; } @@ -624,12 +486,8 @@ public function getTableSpecifications($storeName) * Returns all defined view specification. * * @codeCoverageIgnore - * - * @param string $storeName - * - * @return array */ - public function getViewSpecifications($storeName) + public function getViewSpecifications(string $storeName): array { return $this->viewSpecs[$storeName] ?? []; } @@ -637,11 +495,9 @@ public function getViewSpecifications($storeName) /** * This method returns a unique list of every rdf type configured in a specifications ['type'] restriction. * - * @param string $storeName - * - * @return array of types + * @return string[] array of types */ - public function getAllTypesInSpecifications($storeName) + public function getAllTypesInSpecifications(string $storeName): array { $viewTypes = $this->getTypesInViewSpecifications($storeName); $tableTypes = $this->getTypesInTableSpecifications($storeName); @@ -654,48 +510,39 @@ public function getAllTypesInSpecifications($storeName) /** * Returns a unique list of every rdf type configured in the view spec ['type'] restriction. * - * @param string $storeName - * @param string|null $pod - * - * @return array + * @return string[] */ - public function getTypesInViewSpecifications($storeName, $pod = null) + public function getTypesInViewSpecifications(string $storeName, ?string $pod = null): array { - return array_unique($this->getSpecificationTypes($this->getViewSpecifications($storeName), $pod)); + return array_values(array_unique($this->getSpecificationTypes($this->getViewSpecifications($storeName), $pod))); } /** * Returns a unique list of every rdf type configured in the table spec ['type'] restriction. * - * @param string $storeName - * @param string|null $pod - * - * @return array + * @return string[] */ - public function getTypesInTableSpecifications($storeName, $pod = null) + public function getTypesInTableSpecifications(string $storeName, ?string $pod = null): array { - return array_unique($this->getSpecificationTypes($this->getTableSpecifications($storeName), $pod)); + return array_values(array_unique($this->getSpecificationTypes($this->getTableSpecifications($storeName), $pod))); } /** * Returns a unique list of every rdf type configured in the search doc spec ['type'] restriction. * - * @param string $storeName - * @param string|null $pod - * - * @return array + * @return string[] */ - public function getTypesInSearchSpecifications($storeName, $pod = null) + public function getTypesInSearchSpecifications(string $storeName, ?string $pod = null): array { - return array_unique($this->getSpecificationTypes($this->getSearchDocumentSpecifications($storeName), $pod)); + return array_values(array_unique($this->getSpecificationTypes($this->getSearchDocumentSpecifications($storeName), $pod))); } /** * Returns an array of database names. * - * @return array + * @return string[] */ - public function getDbs() + public function getDbs(): array { return array_keys($this->dbConfig); } @@ -703,46 +550,35 @@ public function getDbs() /** * Returns an array of defined namespaces. * - * @return array + * @return array An associative array of namespaces, keyed by prefix */ - public function getNamespaces() + public function getNamespaces(): array { return $this->ns; } /** * Getter for transaction log connection config. - * - * @return array */ - public function getTransactionLogConfig() + public function getTransactionLogConfig(): array { return $this->tConfig; } - /** - * @param string $storeName - * - * @return string|null - */ - public function getSearchProviderClassName($storeName) + public function getSearchProviderClassName(string $storeName): ?string { return $this->searchProviderClassName[$storeName] ?? null; } /** - * @param string $storeName - * @param string|null $dataSource - * @param string $readPreference - * - * @return Database + * @param int|string $readPreference * * @throws ConfigException */ - public function getDatabase($storeName, $dataSource = null, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED) + public function getDatabase(string $storeName, ?string $dataSource = null, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Database { if (!isset($this->dbConfig[$storeName])) { - throw new ConfigException("Store name '{$storeName}' not in configuration"); + throw new ConfigException(sprintf("Store name '%s' not in configuration", $storeName)); } if (!$dataSource) { @@ -757,15 +593,11 @@ public function getDatabase($storeName, $dataSource = null, $readPreference = Re } /** - * @param string $storeName - * @param string $podName - * @param string $readPreference - * - * @return Collection + * @param int|string $readPreference * * @throws ConfigException */ - public function getCollectionForCBD($storeName, $podName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED) + public function getCollectionForCBD(string $storeName, string $podName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection { if (isset($this->podConnections[$storeName], $this->podConnections[$storeName][$podName])) { return $this->getMongoCollection( @@ -774,95 +606,90 @@ public function getCollectionForCBD($storeName, $podName, $readPreference = Read ); } - throw new ConfigException("Collection name '{$podName}' not in configuration for store '{$storeName}'"); + throw new ConfigException(sprintf("Collection name '%s' not in configuration for store '%s'", $podName, $storeName)); } /** - * @param string $storeName - * @param string $viewId - * @param string $readPreference - * - * @return Collection + * @param int|string $readPreference * * @throws ConfigException */ - public function getCollectionForView($storeName, $viewId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED) + public function getCollectionForView(string $storeName, string $viewId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection { - if (isset($this->viewSpecs[$storeName], $this->viewSpecs[$storeName][$viewId])) { - return $this->getMongoCollection( - $this->getDatabase($storeName, $this->viewSpecs[$storeName][$viewId]['to_data_source'], $readPreference), - VIEWS_COLLECTION - ); + if (!isset($this->viewSpecs[$storeName][$viewId])) { + throw new ConfigException(sprintf("View id '%s' not in configuration for store '%s'", $viewId, $storeName)); } - throw new ConfigException("View id '{$viewId}' not in configuration for store '{$storeName}'"); + $dataSource = $this->viewSpecs[$storeName][$viewId]['to_data_source'] ?? null; + + return $this->getMongoCollection( + $this->getDatabase($storeName, $dataSource, $readPreference), + VIEWS_COLLECTION + ); } /** - * @param string $storeName - * @param string $searchDocumentId - * @param string $readPreference - * - * @return Collection + * @param int|string $readPreference * * @throws ConfigException */ - public function getCollectionForSearchDocument($storeName, $searchDocumentId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED) + public function getCollectionForSearchDocument(string $storeName, string $searchDocumentId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection { - if (array_key_exists($storeName, $this->searchDocSpecs) && array_key_exists($searchDocumentId, $this->searchDocSpecs[$storeName])) { - return $this->getMongoCollection( - $this->getDatabase($storeName, $this->searchDocSpecs[$storeName][$searchDocumentId]['to_data_source'], $readPreference), - SEARCH_INDEX_COLLECTION - ); + if (!isset($this->searchDocSpecs[$storeName][$searchDocumentId])) { + throw new ConfigException(sprintf("Search document id '%s' not in configuration for store '%s'", $searchDocumentId, $storeName)); } - throw new ConfigException("Search document id '{$searchDocumentId}' not in configuration for store '{$storeName}'"); + $dataSource = $this->searchDocSpecs[$storeName][$searchDocumentId]['to_data_source'] ?? null; + + return $this->getMongoCollection( + $this->getDatabase($storeName, $dataSource, $readPreference), + SEARCH_INDEX_COLLECTION + ); } /** - * @param string $storeName - * @param string $tableId - * @param string $readPreference - * - * @return Collection + * @param int|string $readPreference * * @throws ConfigException */ - public function getCollectionForTable($storeName, $tableId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED) + public function getCollectionForTable(string $storeName, string $tableId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection { - if (isset($this->tableSpecs[$storeName][$tableId], $this->tableSpecs[$storeName][$tableId])) { - return $this->getMongoCollection( - $this->getDatabase($storeName, $this->tableSpecs[$storeName][$tableId]['to_data_source'], $readPreference), - TABLE_ROWS_COLLECTION - ); + if (!isset($this->tableSpecs[$storeName][$tableId])) { + throw new ConfigException(sprintf("Table id '%s' not in configuration for store '%s'", $tableId, $storeName)); } - throw new ConfigException("Table id '{$tableId}' not in configuration for store '{$storeName}'"); + $dataSource = $this->tableSpecs[$storeName][$tableId]['to_data_source'] ?? null; + + return $this->getMongoCollection( + $this->getDatabase($storeName, $dataSource, $readPreference), + TABLE_ROWS_COLLECTION + ); } /** - * @param string $storeName - * @param string $readPreference + * @param int|string $readPreference * * @return Collection[] * * @throws ConfigException */ - public function getCollectionsForTables($storeName, array $tables = [], $readPreference = ReadPreference::RP_PRIMARY_PREFERRED) + public function getCollectionsForTables(string $storeName, array $tables = [], $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): array { if (!isset($this->tableSpecs[$storeName])) { return []; } - if (empty($tables)) { + + if ($tables === []) { $tables = array_keys($this->tableSpecs[$storeName]); } + $dataSources = []; foreach ($tables as $table) { - if (isset($this->tableSpecs[$storeName][$table])) { - $dataSources[] = $this->tableSpecs[$storeName][$table]['to_data_source']; - } else { - throw new ConfigException("Table id '{$table}' not in configuration for store '{$storeName}'"); + if (!isset($this->tableSpecs[$storeName][$table])) { + throw new ConfigException(sprintf("Table id '%s' not in configuration for store '%s'", $table, $storeName)); } + + $dataSources[] = $this->tableSpecs[$storeName][$table]['to_data_source'] ?? null; } $collections = []; @@ -877,28 +704,29 @@ public function getCollectionsForTables($storeName, array $tables = [], $readPre } /** - * @param string $storeName - * @param string $readPreference + * @param int|string $readPreference * * @return Collection[] * * @throws ConfigException */ - public function getCollectionsForViews($storeName, array $views = [], $readPreference = ReadPreference::RP_PRIMARY_PREFERRED) + public function getCollectionsForViews(string $storeName, array $views = [], $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): array { if (!isset($this->viewSpecs[$storeName])) { return []; } - if (empty($views)) { + + if ($views === []) { $views = array_keys($this->viewSpecs[$storeName]); } + $dataSources = []; foreach ($views as $view) { - if (isset($this->viewSpecs[$storeName][$view])) { - $dataSources[] = $this->viewSpecs[$storeName][$view]['to_data_source']; - } else { - throw new ConfigException("View id '{$view}' not in configuration for store '{$storeName}'"); + if (!isset($this->viewSpecs[$storeName][$view])) { + throw new ConfigException(sprintf("View id '%s' not in configuration for store '%s'", $view, $storeName)); } + + $dataSources[] = $this->viewSpecs[$storeName][$view]['to_data_source'] ?? null; } $collections = []; @@ -913,28 +741,29 @@ public function getCollectionsForViews($storeName, array $views = [], $readPrefe } /** - * @param string $storeName - * @param string $readPreference + * @param int|string $readPreference * * @return Collection[] * * @throws ConfigException */ - public function getCollectionsForSearch($storeName, array $searchSpecIds = [], $readPreference = ReadPreference::RP_PRIMARY_PREFERRED) + public function getCollectionsForSearch(string $storeName, array $searchSpecIds = [], $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): array { if (!isset($this->searchDocSpecs[$storeName])) { return []; } - if (empty($searchSpecIds)) { + + if ($searchSpecIds === []) { $searchSpecIds = array_keys($this->searchDocSpecs[$storeName]); } + $dataSources = []; foreach ($searchSpecIds as $searchSpec) { - if (isset($this->searchDocSpecs[$storeName][$searchSpec])) { - $dataSources[] = $this->searchDocSpecs[$storeName][$searchSpec]['to_data_source']; - } else { - throw new ConfigException("Search document spec id '{$searchSpec}' not in configuration for store '{$storeName}'"); + if (!isset($this->searchDocSpecs[$storeName][$searchSpec])) { + throw new ConfigException(sprintf("Search document spec id '%s' not in configuration for store '%s'", $searchSpec, $storeName)); } + + $dataSources[] = $this->searchDocSpecs[$storeName][$searchSpec]['to_data_source'] ?? null; } $collections = []; @@ -949,12 +778,9 @@ public function getCollectionsForSearch($storeName, array $searchSpecIds = [], $ } /** - * @param string $storeName - * @param string $readPreference - * - * @return Collection + * @param int|string $readPreference */ - public function getCollectionForTTLCache($storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED) + public function getCollectionForTTLCache(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection { return $this->getMongoCollection( $this->getDatabase($storeName, $this->dbConfig[$storeName]['data_source'], $readPreference), @@ -963,12 +789,9 @@ public function getCollectionForTTLCache($storeName, $readPreference = ReadPrefe } /** - * @param string $storeName - * @param string $readPreference - * - * @return Collection + * @param int|string $readPreference */ - public function getCollectionForLocks($storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED) + public function getCollectionForLocks(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection { return $this->getMongoCollection( $this->getDatabase($storeName, $this->dbConfig[$storeName]['data_source'], $readPreference), @@ -977,12 +800,9 @@ public function getCollectionForLocks($storeName, $readPreference = ReadPreferen } /** - * @param string $storeName - * @param string $readPreference - * - * @return Collection + * @param int|string $readPreference */ - public function getCollectionForManualRollbackAudit($storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED) + public function getCollectionForManualRollbackAudit(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection { return $this->getMongoCollection( $this->getDatabase($storeName, $this->dbConfig[$storeName]['data_source'], $readPreference), @@ -991,12 +811,9 @@ public function getCollectionForManualRollbackAudit($storeName, $readPreference } /** - * @param string $storeName - * @param string $readPreference - * - * @return Collection + * @param int|string $readPreference */ - public function getCollectionForJobGroups($storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED) + public function getCollectionForJobGroups(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection { return $this->getMongoCollection( $this->getDatabase($storeName, $this->dbConfig[$storeName]['data_source'], $readPreference), @@ -1005,13 +822,11 @@ public function getCollectionForJobGroups($storeName, $readPreference = ReadPref } /** - * @param string $readPreference - * - * @return Database + * @param int|string $readPreference * * @throws ConfigException */ - public function getTransactionLogDatabase($readPreference = ReadPreference::RP_PRIMARY_PREFERRED) + public function getTransactionLogDatabase($readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Database { $client = $this->getConnectionForDataSource($this->tConfig['data_source']); $db = $client->selectDatabase($this->tConfig['database']); @@ -1019,34 +834,22 @@ public function getTransactionLogDatabase($readPreference = ReadPreference::RP_P return $db->withOptions(['readPreference' => new ReadPreference($readPreference)]); } - /** - * @return string - */ - public static function getDiscoverQueueName() + public static function getDiscoverQueueName(): string { return self::getQueueName(TRIPOD_DISCOVER_QUEUE, 'discover'); } - /** - * @return string - */ - public static function getApplyQueueName() + public static function getApplyQueueName(): string { return self::getQueueName(TRIPOD_APPLY_QUEUE, 'apply'); } - /** - * @return string - */ - public static function getEnsureIndexesQueueName() + public static function getEnsureIndexesQueueName(): string { return self::getQueueName(TRIPOD_ENSURE_INDEXES_QUEUE, 'ensureindexes'); } - /** - * @return string - */ - public static function getResqueServer() + public static function getResqueServer(): string { $resqueServer = self::getenv(RESQUE_SERVER, ''); if (empty($resqueServer)) { @@ -1055,6 +858,7 @@ public static function getResqueServer() self::getLogger()->notice('Use of MONGO_TRIPOD_RESQUE_SERVER is deprecated - use RESQUE_SERVER instead'); } } + if (empty($resqueServer)) { self::getLogger()->warning('RESQUE_SERVER is missing from environment - using localhost:6379 instead'); $resqueServer = 'localhost:6379'; @@ -1063,12 +867,7 @@ public static function getResqueServer() return $resqueServer; } - /** - * @static - * - * @return LoggerInterface - */ - public static function getLogger() + public static function getLogger(): LoggerInterface { if (self::$logger == null) { $log = new Logger('TRIPOD'); @@ -1080,14 +879,13 @@ public static function getLogger() /** * Sets the Tripod config. - * - * @return Config */ - public static function deserialize(array $config) + public static function deserialize(array $config): IConfigInstance { if (isset($config['class'], $config['config'])) { $config = $config['config']; } + $instance = new self(); $instance->loadConfig($config); @@ -1096,10 +894,8 @@ public static function deserialize(array $config) /** * Serializes the config into an array that can be passed to jobs, etc. - * - * @return array */ - public function serialize() + public function serialize(): array { return $this->config; } @@ -1108,10 +904,8 @@ public function serialize() * Return the maximum batch size for async operations. * * @param string $operation Async operation, e.g. OP_TABLES, OP_VIEWS - * - * @return int */ - public function getBatchSize($operation) + public function getBatchSize(string $operation): int { return $this->batchSize[$operation] ?? 1; } @@ -1119,24 +913,28 @@ public function getBatchSize($operation) /** * Used to load the config from self::config when new instance is generated. * + * @param array $config + * * @throws ConfigException */ - protected function loadConfig(array $config) + protected function loadConfig(array $config): void { $this->config = $config; - if (array_key_exists('namespaces', $config)) { + if (isset($config['namespaces'])) { $this->ns = $config['namespaces']; } $this->defaultContext = $this->getMandatoryKey('defaultContext', $config); foreach ($this->getMandatoryKey('data_sources', $config) as $source => $c) { - if (!array_key_exists('type', $c)) { - throw new ConfigException("No 'type' set for data source {$source}"); + if (!isset($c['type'])) { + throw new ConfigException("No 'type' set for data source " . $source); } - if (!array_key_exists('connection', $c)) { - throw new ConfigException("No connection information set for data source {$source}"); + + if (!isset($c['connection'])) { + throw new ConfigException('No connection information set for data source ' . $source); } + $this->dataSources[$source] = $c; } @@ -1145,12 +943,12 @@ protected function loadConfig(array $config) if (!isset($this->dataSources[$this->tConfig['data_source']])) { throw new ConfigException('Transaction log data source, ' . $this->tConfig['data_source'] . ', was not defined'); } + $this->tConfig['database'] = $this->getMandatoryKey('database', $transactionConfig, 'transaction_log'); $this->tConfig['collection'] = $this->getMandatoryKey('collection', $transactionConfig, 'transaction_log'); // A 'pod' corresponds to a logical database $this->databases = $this->getMandatoryKey('stores', $config); - $defaultDB = null; foreach ($this->databases as $storeName => $storeConfig) { $this->dbConfig[$storeName] = ['data_source' => $this->getMandatoryKey('data_source', $storeConfig)]; if (isset($storeConfig['database']) && !empty($storeConfig['database'])) { @@ -1168,7 +966,7 @@ protected function loadConfig(array $config) $this->podConnections[$storeName][$podName] = $dataSource; // Set cardinality, also checking against defined namespaces - if (array_key_exists('cardinality', $podConfig)) { + if (isset($podConfig['cardinality'])) { // Test that the namespace exists for each cardinality rule defined $cardinality = $podConfig['cardinality']; foreach ($cardinality as $qname => $cardinalityValue) { @@ -1176,20 +974,20 @@ protected function loadConfig(array $config) // just grab the first element $namespace = array_shift($namespaces); - if (array_key_exists($namespace, $this->ns)) { + if (isset($this->ns[$namespace])) { $this->cardinality[$storeName][$podName][] = $cardinality; } else { - throw new ConfigException("Cardinality '{$qname}' does not have the namespace defined"); + throw new ConfigException(sprintf("Cardinality '%s' does not have the namespace defined", $qname)); } } } else { $this->cardinality[$storeName][$podName] = []; } - $this->cardinality[$storeName][$podName] = array_key_exists('cardinality', $podConfig) ? $podConfig['cardinality'] : []; + $this->cardinality[$storeName][$podName] = $podConfig['cardinality'] ?? []; // Ensure indexes are legal - if (array_key_exists('indexes', $podConfig)) { + if (isset($podConfig['indexes'])) { $this->indexes[$storeName][$podName] = []; foreach ($podConfig['indexes'] as $indexName => $indexFields) { @@ -1209,12 +1007,15 @@ protected function loadConfig(array $config) $fieldsThatAreArrays = 0; foreach ($cardinalityIndexFields as $field => $fieldVal) { $cardinalityField = str_replace('.value', '', $field); - if (!array_key_exists($cardinalityField, $this->cardinality[$storeName][$podName]) - || $this->cardinality[$storeName][$podName][$cardinalityField] != 1) { + if ( + !isset($this->cardinality[$storeName][$podName][$cardinalityField]) + || $this->cardinality[$storeName][$podName][$cardinalityField] != 1 + ) { $fieldsThatAreArrays++; } + if ($fieldsThatAreArrays > 1) { - throw new ConfigException("Compound index {$indexName} has more than one field with cardinality > 1 - mongo will not be able to build this index"); + throw new ConfigException(sprintf('Compound index %s has more than one field with cardinality > 1 - mongo will not be able to build this index', $indexName)); } } } @@ -1222,6 +1023,7 @@ protected function loadConfig(array $config) } } } + if (isset($storeConfig['batch_sizes'])) { foreach ([OP_TABLES, OP_SEARCH, OP_VIEWS] as $op) { if (isset($storeConfig['batch_sizes'][$op]) && is_numeric($storeConfig['batch_sizes'][$op])) { @@ -1229,7 +1031,8 @@ protected function loadConfig(array $config) } } } - $searchConfig = array_key_exists('search_config', $storeConfig) ? $storeConfig['search_config'] : []; + + $searchConfig = $storeConfig['search_config'] ?? []; $this->searchDocSpecs[$storeName] = []; if (!empty($searchConfig)) { $this->searchProviderClassName[$storeName] = $this->getMandatoryKey('search_provider', $searchConfig, 'search'); @@ -1239,9 +1042,11 @@ protected function loadConfig(array $config) if (!isset($spec[_ID_KEY])) { throw new ConfigException('Search document spec does not contain ' . _ID_KEY); } + if (!isset($spec['from']) || !in_array($spec['from'], $this->getPods($storeName))) { throw new ConfigException("'" . $spec[_ID_KEY] . "[\"from\"]' property not set or references an undefined pod"); } + if (!isset($spec['filter'])) { throw new ConfigException("'" . $spec[_ID_KEY] . "[\"filter\"]' property not set"); } @@ -1259,23 +1064,27 @@ protected function loadConfig(array $config) $spec['to_data_source'] = $storeConfig['data_source']; } } + $this->searchDocSpecs[$storeName][$spec[_ID_KEY]] = $spec; } } // Load view specs - $viewSpecs = (array_key_exists('view_specifications', $storeConfig)) ? $storeConfig['view_specifications'] : []; + $viewSpecs = $storeConfig['view_specifications'] ?? []; $this->viewSpecs[$storeName] = []; foreach ($viewSpecs as $spec) { if (!isset($spec[_ID_KEY])) { throw new ConfigException('View spec does not contain ' . _ID_KEY); } + if (!isset($spec['from']) || !in_array($spec['from'], $this->getPods($storeName))) { throw new ConfigException("'" . $spec[_ID_KEY] . "[\"from\"]' property not set or references an undefined pod"); } + if (!isset($spec['joins'])) { throw new ConfigException('Could not find any joins in view specification - usecase better served with select()'); } + $this->ifCountExistsWithoutTTLThrowException($spec); if (isset($spec['to_data_source'])) { if (!isset($this->dataSources[$spec['to_data_source']])) { @@ -1284,11 +1093,12 @@ protected function loadConfig(array $config) } else { $spec['to_data_source'] = $storeConfig['data_source']; } + $this->viewSpecs[$storeName][$spec[_ID_KEY]] = $spec; } // Load table specs - $tableSpecs = (array_key_exists('table_specifications', $storeConfig)) ? $storeConfig['table_specifications'] : []; + $tableSpecs = $storeConfig['table_specifications'] ?? []; $this->tableSpecs[$storeName] = []; foreach ($tableSpecs as $spec) { $this->validateTableSpec($spec); @@ -1307,16 +1117,17 @@ protected function loadConfig(array $config) } /** - * @param int $depth + * @param array $spec * * @throws ConfigException */ - protected function validateTableSpecPart(array $spec, $depth = 0) + protected function validateTableSpecPart(array $spec, int $depth = 0): void { $validationLevel = $this->getValidationLevel(); if (!isset($spec['fields']) && !isset($spec['joins']) && !isset($spec['counts']) && !isset($spec['computed_fields'])) { throw new ConfigException('Table spec part does not contain fields, joins, counts, or computed_fields'); } + if (isset($spec['fields'])) { foreach ($spec['fields'] as $field) { if (!isset($field['fieldName'])) { @@ -1324,7 +1135,7 @@ protected function validateTableSpecPart(array $spec, $depth = 0) } if (isset($field['predicates'])) { - if ($validationLevel == self::VALIDATE_MAX) { + if ($validationLevel === self::VALIDATE_MAX) { foreach ($field['predicates'] as $p) { // If predicates is an array we've got modifiers if (is_array($p)) { @@ -1366,14 +1177,16 @@ protected function validateTableSpecPart(array $spec, $depth = 0) } $validComputingFieldFunctions = Tables::$computedFieldFunctions; - if ($validationLevel == self::VALIDATE_MAX) { + if ($validationLevel === self::VALIDATE_MAX) { $availableFields = $this->getFieldNamesInSpec($spec); - $availableFields = array_map(function ($field) { return '$' . $field; }, $availableFields); + $availableFields = array_map(fn (string $field): string => '$' . $field, $availableFields); } + foreach ($spec['computed_fields'] as $field) { if (!isset($field['fieldName'])) { throw new ConfigException('Computed field spec does not contain fieldName'); } + if (!isset($field['value'])) { throw new ConfigException('Computed field spec does not contain value'); } @@ -1384,14 +1197,15 @@ protected function validateTableSpecPart(array $spec, $depth = 0) $functions = array_intersect(array_keys($field['value']), $validComputingFieldFunctions); - if (empty($functions)) { + if ($functions === []) { throw new ConfigException('Computed field spec does not contain valid function'); } if (count($functions) > 1) { throw new ConfigException('Computed field spec contains more than one function'); } - if ($validationLevel == self::VALIDATE_MAX) { + + if ($validationLevel === self::VALIDATE_MAX && isset($availableFields)) { $this->validateComputedFieldSpec($functions[0], $field['value'], $availableFields); } } @@ -1406,9 +1220,10 @@ protected function validateTableSpecPart(array $spec, $depth = 0) } /** - * @param string $type + * @param string[] $availableFields + * @param array $spec */ - protected function validateComputedFieldSpec($type, array $spec, array $availableFields) + protected function validateComputedFieldSpec(string $type, array $spec, array $availableFields): void { switch ($type) { case 'conditional': @@ -1429,9 +1244,12 @@ protected function validateComputedFieldSpec($type, array $spec, array $availabl } /** + * @param array $spec + * @param string[] $availableFields + * * @throws ConfigException */ - protected function validateComputedConditionalSpec(array $spec, array $availableFields) + protected function validateComputedConditionalSpec(array $spec, array $availableFields): void { if (!isset($spec['if'])) { throw new ConfigException("Computed conditional spec does not contain an 'if' value"); @@ -1475,11 +1293,10 @@ protected function validateComputedConditionalSpec(array $spec, array $available default: throw new ConfigException("Computed conditional field 'then' value has more than one function"); - - break; } } } + if (isset($spec['else'])) { if (is_string($spec['else'])) { $this->validateSpecVariableReplacement($spec['else'], $availableFields); @@ -1497,8 +1314,6 @@ protected function validateComputedConditionalSpec(array $spec, array $available default: throw new ConfigException("Computed conditional field 'else' value has more than one function"); - - break; } } } @@ -1506,16 +1321,15 @@ protected function validateComputedConditionalSpec(array $spec, array $available /** * @param array|string $value + * @param string[] $availableFields * * @throws ConfigException */ - protected function validateSpecVariableReplacement($value, array $availableFields) + protected function validateSpecVariableReplacement($value, array $availableFields): void { if (is_string($value)) { - if (strpos($value, '$') === 0) { - if (!in_array($value, $availableFields)) { - throw new ConfigException("Computed spec variable '{$value}' is not defined in table spec"); - } + if (strpos($value, '$') === 0 && !in_array($value, $availableFields)) { + throw new ConfigException(sprintf("Computed spec variable '%s' is not defined in table spec", $value)); } } elseif (is_array($value)) { foreach ($value as $v) { @@ -1525,32 +1339,42 @@ protected function validateSpecVariableReplacement($value, array $availableField } /** + * @param array $spec + * @param string[] $availableFields + * * @throws ConfigException */ - protected function validateComputedReplaceSpec(array $spec, array $availableFields) + protected function validateComputedReplaceSpec(array $spec, array $availableFields): void { if (!isset($spec['search'])) { throw new ConfigException("Computed replace spec does not contain 'search' value"); } + $this->validateSpecVariableReplacement($spec['search'], $availableFields); if (!isset($spec['replace'])) { throw new ConfigException("Computed replace spec does not contain 'replace' value"); } + $this->validateSpecVariableReplacement($spec['replace'], $availableFields); if (!isset($spec['subject'])) { throw new ConfigException("Computed replace spec does not contain 'subject' value"); } + $this->validateSpecVariableReplacement($spec['subject'], $availableFields); } /** + * @param array $spec + * @param string[] $availableFields + * * @throws ConfigException */ - protected function validateComputedArithmeticSpec(array $spec, array $availableFields) + protected function validateComputedArithmeticSpec(array $spec, array $availableFields): void { if (count($spec) !== 3) { throw new ConfigException('Computed arithmetic spec must contain 3 values'); } + if (is_array($spec[0])) { if (count(array_keys($spec[0])) === 1 && count(array_intersect(array_keys($spec[0]), Tables::$computedFieldFunctions)) === 1) { $function = array_keys($spec[0]); @@ -1561,6 +1385,7 @@ protected function validateComputedArithmeticSpec(array $spec, array $availableF } else { $this->validateSpecVariableReplacement($spec[0], $availableFields); } + if (is_array($spec[2])) { if (count(array_keys($spec[2])) === 1 && count(array_intersect(array_keys($spec[2]), Tables::$computedFieldFunctions)) === 1) { $function = array_keys($spec[2]); @@ -1571,15 +1396,16 @@ protected function validateComputedArithmeticSpec(array $spec, array $availableF } else { $this->validateSpecVariableReplacement($spec[2], $availableFields); } + if (!in_array($spec[1], Tables::$arithmeticOperators)) { throw new ConfigException("Invalid arithmetic operator '" . $spec[1] . "' in computed arithmetic spec"); } } /** - * @return array + * @param array $spec */ - protected function getFieldNamesInSpec(array $spec) + protected function getFieldNamesInSpec(array $spec): array { $fieldNames = []; if (isset($spec['fields'])) { @@ -1589,6 +1415,7 @@ protected function getFieldNamesInSpec(array $spec) } } } + if (isset($spec['counts'])) { foreach ($spec['counts'] as $count) { if (isset($count['fieldName'])) { @@ -1616,12 +1443,8 @@ protected function getFieldNamesInSpec(array $spec) /** * Creates an associative array of all predicates/properties associated with all table and search document specifications. - * - * @param string $storename - * - * @return array */ - protected function getDefinedPredicatesInSpecs($storename) + protected function getDefinedPredicatesInSpecs(string $storename): array { $predicates = []; $specs = array_merge($this->getTableSpecifications($storename), $this->getSearchDocumentSpecifications($storename)); @@ -1629,6 +1452,7 @@ protected function getDefinedPredicatesInSpecs($storename) if (!isset($spec[_ID_KEY])) { continue; } + $predicates[$spec[_ID_KEY]] = array_unique($this->getDefinedPredicatesInSpecBlock($spec)); } @@ -1638,15 +1462,16 @@ protected function getDefinedPredicatesInSpecs($storename) /** * Recursively crawls a configuration document array (or part of one) and returns any associated predicates/properties. * - * @return array + * @param array $block */ - protected function getDefinedPredicatesInSpecBlock(array $block) + protected function getDefinedPredicatesInSpecBlock(array $block): array { $predicates = []; // If the spec has a "type" property, include rdf:type if (isset($block['type'])) { $predicates[] = $this->getLabeller()->uri_to_alias(RDF_TYPE); } + if (isset($block['filter'])) { foreach ($block['filter'] as $filter) { if (isset($filter['condition'])) { @@ -1654,6 +1479,7 @@ protected function getDefinedPredicatesInSpecBlock(array $block) } } } + // Get the predicates out of the defined fields if (isset($block['fields'])) { foreach ($block['fields'] as $field) { @@ -1717,13 +1543,11 @@ protected function getDefinedPredicatesInSpecBlock(array $block) * Rewrites any predicate uris to alias curies. * * @param array|string $predicate - * - * @return array */ - protected function getPredicateAliasesFromPredicateProperty($predicate) + protected function getPredicateAliasesFromPredicateProperty($predicate): array { $predicates = []; - if (is_string($predicate) && !empty($predicate)) { + if (is_string($predicate) && ($predicate !== '' && $predicate !== '0')) { $predicates[] = $this->getLabeller()->uri_to_alias($predicate); } elseif (is_array($predicate)) { foreach ($this->getPredicatesFromPredicateFunctions($predicate) as $p) { @@ -1736,19 +1560,16 @@ protected function getPredicateAliasesFromPredicateProperty($predicate) /** * When given an array as input, will traverse any predicate functions and return the predicate strings. - * - * @param array $array - * - * @return array */ - protected function getPredicatesFromPredicateFunctions($array) + protected function getPredicatesFromPredicateFunctions(array $array): array { $predicates = []; - if (is_array($array)) { - if (isset($array['predicates'])) { - $predicates = $array['predicates']; - } else { - $predicates = array_merge($predicates, $this->getPredicatesFromPredicateFunctions($array[key($array)])); + if (isset($array['predicates'])) { + $predicates = $array['predicates']; + } elseif ($array !== []) { + $function = key($array); + if (is_array($array[$function])) { + $predicates = array_merge($predicates, $this->getPredicatesFromPredicateFunctions($array[$function])); } } @@ -1758,11 +1579,9 @@ protected function getPredicatesFromPredicateFunctions($array) /** * Parses a specDocument's "filter" parameter for any predicates. * - * @param array $filter - * - * @return array + * @return string[] */ - protected function getPredicatesFromFilterCondition($filter) + protected function getPredicatesFromFilterCondition(array $filter): array { $predicates = []; $regex = '/(^|\b)(\w+\:\w+)\.(l|u)(\b|$)/'; @@ -1775,6 +1594,7 @@ protected function getPredicatesFromFilterCondition($filter) } } } + if (is_string($condition)) { $numMatches = preg_match_all($regex, $condition, $matches); for ($i = 0; $i < $numMatches; $i++) { @@ -1790,10 +1610,7 @@ protected function getPredicatesFromFilterCondition($filter) return $predicates; } - /** - * @return Labeller - */ - protected function getLabeller() + protected function getLabeller(): Labeller { if ($this->labeller == null) { $this->labeller = new Labeller(); @@ -1803,18 +1620,15 @@ protected function getLabeller() } /** - * @param string $dataSource - * - * @return Client - * * @throws ConfigException * @throws ConnectionTimeoutException */ - protected function getConnectionForDataSource($dataSource) + protected function getConnectionForDataSource(string $dataSource): Client { if (!isset($this->dataSources[$dataSource])) { - throw new ConfigException("Data source '{$dataSource}' not in configuration"); + throw new ConfigException(sprintf("Data source '%s' not in configuration", $dataSource)); } + if (!isset($this->connections[$dataSource])) { $ds = $this->dataSources[$dataSource]; $connectionString = $ds['connection']; @@ -1844,10 +1658,10 @@ protected function getConnectionForDataSource($dataSource) } } while ($retries <= self::CONNECTION_RETRIES); - if (!isset($this->connections[$dataSource])) { - self::getLogger()->error('MongoConnectionException failed after ' . $retries . ' attempts (MAX:' . self::CONNECTION_RETRIES . '): ' . $e->getMessage()); + if (!isset($this->connections[$dataSource]) && $exception !== null) { + self::getLogger()->error('MongoConnectionException failed after ' . $retries . ' attempts (MAX:' . self::CONNECTION_RETRIES . '): ' . $exception->getMessage()); - throw new ConnectionTimeoutException($exception); + throw $exception; } } @@ -1856,12 +1670,8 @@ protected function getConnectionForDataSource($dataSource) /** * Create a Mongo Client - used for mocking. - * - * @param string $connectionString - * - * @return Client */ - protected function getMongoClient($connectionString, array $connectionOptions = []) + protected function getMongoClient(string $connectionString, array $connectionOptions = []): Client { return new Client( $connectionString, @@ -1870,12 +1680,7 @@ protected function getMongoClient($connectionString, array $connectionOptions = ); } - /** - * @param string $collectionName - * - * @return Collection - */ - protected function getMongoCollection(Database $db, $collectionName) + protected function getMongoCollection(Database $db, string $collectionName): Collection { return $db->selectCollection($collectionName); } @@ -1884,18 +1689,15 @@ protected function getMongoCollection(Database $db, $collectionName) /** * Returns a unique list of every rdf type configured in the supplied specs' ['type'] restriction. * - * @param string|null $podName - * - * @return array + * @return string[] */ - private function getSpecificationTypes(array $specifications, $podName = null) + private function getSpecificationTypes(array $specifications, ?string $podName = null): array { $types = []; foreach ($specifications as $spec) { - if (!empty($podName)) { - if ($spec['from'] !== $podName) { - continue; // skip this view spec if it isnt for the collection - } + if (!empty($podName) && $spec['from'] !== $podName) { + continue; + // skip this view spec if it isnt for the collection } if (is_array($spec[_ID_TYPE])) { @@ -1912,23 +1714,24 @@ private function getSpecificationTypes(array $specifications, $podName = null) * If we have 'counts' in a view spec, a 'ttl' must be defined. * Note: this does not apply to tables or search docs. * - * @param array $spec + * @param array $spec * * @throws ConfigException */ - private function ifCountExistsWithoutTTLThrowException($spec) + private function ifCountExistsWithoutTTLThrowException(array $spec): void { - if (array_key_exists('ttl', $spec)) { + if (isset($spec['ttl'])) { return; // ttl exists } - if (array_key_exists('joins', $spec)) { + if (isset($spec['joins'])) { // recurse foreach ($spec['joins'] as $join) { $this->ifCountExistsWithoutTTLThrowException($join); } } - if (array_key_exists('counts', $spec)) { + + if (isset($spec['counts'])) { throw new ConfigException('Aggregate function counts exists in spec, but no TTL defined'); } } @@ -1936,79 +1739,46 @@ private function ifCountExistsWithoutTTLThrowException($spec) /** * Returns the value of the supplied key or throws an error, if missing. * - * @param string $key - * @param string $configName + * @param array $a * * @return mixed * * @throws ConfigException */ - private function getMandatoryKey($key, array $a, $configName = 'config') + private function getMandatoryKey(string $key, array $a, string $configName = 'config') { if (!array_key_exists($key, $a)) { - throw new ConfigException("Mandatory config key [{$key}] is missing from {$configName}"); + throw new ConfigException(sprintf('Mandatory config key [%s] is missing from %s', $key, $configName)); } return $a[$key]; } - /** - * Finds fields in a table specification. - * - * @param string $fieldName - * @param array $spec, a part of space ot complete spec - * - * @return array - */ - private function findFieldsInTableSpec($fieldName, $spec) - { - $fields = []; - if (is_array($spec) && !empty($spec)) { - if (array_key_exists($fieldName, $spec)) { - $fields = $spec[$fieldName]; - } - - if (isset($spec['joins'])) { - foreach ($spec['joins'] as $join) { - $fields = array_merge($fields, $this->findFieldsInTableSpec($fieldName, $join)); - } - } - } - - return $fields; - } - - /** - * @param string $envVar - * @param string $type - * - * @return string - */ - private static function getQueueName($envVar, $type) + private static function getQueueName(string $envVar, string $type): string { - $default = (defined('APP_ENV')) ? 'tripod::' . constant('APP_ENV') . "::{$type}" : "tripod::{$type}"; + $default = defined('APP_ENV') ? 'tripod::' . constant('APP_ENV') . ('::' . $type) : 'tripod::' . $type; return self::getenv($envVar, $default); } /** - * @param string $env - * @param bool $default + * @param bool|string $default * * @return bool|string * * @throws ConfigException */ - private static function getenv($env, $default = false) + private static function getenv(string $env, $default = false) { $var = getenv($env); - if (isset($var) && $var != '') { + if ($var) { return $var; } + if ($default !== false) { return $default; } - throw new ConfigException("Missing value for environmental variable {$env}"); + throw new ConfigException('Missing value for environmental variable ' . $env); } } diff --git a/src/mongo/Driver.php b/src/mongo/Driver.php index 468b68f6..061ce07f 100755 --- a/src/mongo/Driver.php +++ b/src/mongo/Driver.php @@ -1,8 +1,8 @@ - *
  • defaultContext: (string) to use where a specific default context is not defined. Default is Null
  • - *
  • async: (array) determines the async behaviour of views, tables and search. For each of these array keys, if set to true, generation of these elements will be done asyncronously on save. Default is array(OP_VIEWS=>false,OP_TABLES=>true,OP_SEARCH=>true)
  • - *
  • stat: this sets the stats object to use to record statistics around operations performed by Driver. Default is null
  • - *
  • readPreference: The Read preference to set for Mongo: Default is ReadPreference::RP_PRIMARY_PREFERRED
  • - *
  • retriesToGetLock: Retries to do when unable to get lock on a document, default is 20
  • + * @param array $opts an Array of options:
      + *
    • defaultContext: (string) to use where a specific default context is not defined. Default is Null
    • + *
    • async: (array) determines the async behaviour of views, tables and search. For each of these array keys, if set to true, generation of these elements will be done asyncronously on save. Default is array(OP_VIEWS=>false,OP_TABLES=>true,OP_SEARCH=>true)
    • + *
    • stat: this sets the stats object to use to record statistics around operations performed by Driver. Default is null
    • + *
    • readPreference: The Read preference to set for Mongo: Default is ReadPreference::RP_PRIMARY_PREFERRED
    • + *
    • retriesToGetLock: Retries to do when unable to get lock on a document, default is 20
    */ - public function __construct($podName, $storeName, $opts = []) + public function __construct(string $podName, string $storeName, array $opts = []) { $opts = array_merge([ 'defaultContext' => null, @@ -91,6 +73,7 @@ public function __construct($podName, $storeName, $opts = []) if (!array_key_exists(OP_VIEWS, $async)) { $async[OP_VIEWS] = false; } + if (!array_key_exists(OP_TABLES, $async)) { $async[OP_TABLES] = true; } @@ -127,7 +110,7 @@ public function __construct($podName, $storeName, $opts = []) * * @return MongoGraph */ - public function describeResource($resource, $context = null) + public function describeResource(string $resource, ?string $context = null): ExtendedGraph { $resource = $this->labeller->uri_to_alias($resource); $query = [ @@ -143,11 +126,9 @@ public function describeResource($resource, $context = null) /** * Pass subjects as to $resources and have mongo return a DESCRIBE etc. * - * @param string|null $context - * * @return MongoGraph */ - public function describeResources(array $resources, $context = null) + public function describeResources(array $resources, ?string $context = null): ExtendedGraph { $ids = []; foreach ($resources as $resource) { @@ -157,57 +138,46 @@ public function describeResources(array $resources, $context = null) _ID_CONTEXT => $this->getContextAlias($context), ]; } + $query = ['_id' => ['$in' => $ids]]; return $this->fetchGraph($query, MONGO_MULTIDESCRIBE); } /** - * @param string $resource - * @param string $viewType - * * @return MongoGraph */ - public function getViewForResource($resource, $viewType) + public function getViewForResource(?string $resource, string $viewType): ExtendedGraph { return $this->getTripodViews()->getViewForResource($resource, $viewType); } /** - * @param string $viewType + * @param string[] $resources * * @return MongoGraph */ - public function getViewForResources(array $resources, $viewType) + public function getViewForResources(array $resources, string $viewType): ExtendedGraph { return $this->getTripodViews()->getViewForResources($resources, $viewType); } /** - * @param string $viewType - * * @return MongoGraph */ - public function getViews(array $filter, $viewType) + public function getViews(array $filter, string $viewType): ExtendedGraph { return $this->getTripodViews()->getViews($filter, $viewType); } - /** - * @param string $tableType - * @param int $offset - * @param int $limit - * - * @return array - */ public function getTableRows( - $tableType, + string $tableType, array $filter = [], - array $sortBy = [], - $offset = 0, - $limit = 10, + ?array $sortBy = [], + ?int $offset = 0, + ?int $limit = 10, array $options = [] - ) { + ): array { return $this->getTripodTables()->getTableRows( $tableType, $filter, @@ -218,23 +188,12 @@ public function getTableRows( ); } - /** - * @param string $tableType - * @param string|null $resource - * @param string|null $context - */ - public function generateTableRows($tableType, $resource = null, $context = null) + public function generateTableRows(string $tableType, ?string $resource = null, ?string $context = null): void { $this->getTripodTables()->generateTableRows($tableType, $resource, $context); } - /** - * @param string $tableType - * @param string $fieldName - * - * @return array - */ - public function getDistinctTableColumnValues($tableType, $fieldName, array $filter = []) + public function getDistinctTableColumnValues(string $tableType, string $fieldName, array $filter = []): array { return $this->getTripodTables()->distinct($tableType, $fieldName, $filter); } @@ -242,31 +201,21 @@ public function getDistinctTableColumnValues($tableType, $fieldName, array $filt /** * Create and apply a changeset which is the delta between $oldGraph and $newGraph. * - * @param string $context - * @param string|null $description - * - * @return bool - * * @throws Exception */ public function saveChanges( ExtendedGraph $oldGraph, ExtendedGraph $newGraph, - $context = null, - $description = null - ) { + ?string $context = null, + ?string $description = null + ): bool { return $this->getDataUpdater()->saveChanges($oldGraph, $newGraph, $context, $description); } /** * Get locked documents for a date range or all documents if no date range is given. - * - * @param string $fromDateTime - * @param string $tillDateTime - * - * @return array */ - public function getLockedDocuments($fromDateTime = null, $tillDateTime = null) + public function getLockedDocuments(?string $fromDateTime = null, ?string $tillDateTime = null): array { return $this->getDataUpdater()->getLockedDocuments($fromDateTime, $tillDateTime); } @@ -274,14 +223,9 @@ public function getLockedDocuments($fromDateTime = null, $tillDateTime = null) /** * Remove locks that are there forever, creates a audit entry to keep track who and why removed these locks. * - * @param string $transaction_id - * @param string $reason - * - * @return bool - * - * @throws \Exception, if something goes wrong when unlocking documents, or creating audit entries + * @throws \Exception If something goes wrong when unlocking documents, or creating audit entries */ - public function removeInertLocks($transaction_id, $reason) + public function removeInertLocks(string $transaction_id, string $reason): bool { return $this->getDataUpdater()->removeInertLocks($transaction_id, $reason); } @@ -304,7 +248,7 @@ public function removeInertLocks($transaction_id, $reason) * @throws Exception - if search provider cannot be found * @throws SearchException - if something goes wrong */ - public function search(array $params) + public function search(array $params): array { $q = $params['q']; $type = $params['type']; @@ -330,19 +274,18 @@ public function search(array $params) return $results; } - throw new Exception("Unknown Search Provider: {$provider}"); + throw new Exception('Unknown Search Provider: ' . $provider); } /** * Returns a count according to the $query and $groupBy conditions. * - * @param array $query Mongo query object - * @param string|null $groupBy - * @param int|null $ttl acceptable time to live if you're willing to accept a cached version of this request + * @param array $query Mongo query object + * @param int|null $ttl acceptable time to live if you're willing to accept a cached version of this request * * @return array|int */ - public function getCount(array $query, $groupBy = null, $ttl = null) + public function getCount(array $query, ?string $groupBy = null, ?int $ttl = null) { $t = new Timer(); $t->start(); @@ -368,6 +311,7 @@ public function getCount(array $query, $groupBy = null, $ttl = null) } } } + if (empty($results)) { if ($groupBy) { $ops = [ @@ -403,7 +347,7 @@ public function getCount(array $query, $groupBy = null, $ttl = null) $t->stop(); $op = ($groupBy) ? MONGO_GROUP : MONGO_COUNT; $this->timingLog($op, ['duration' => $t->result(), 'query' => $query]); - $this->getStat()->timer("{$op}.{$this->podName}", $t->result()); + $this->getStat()->timer(sprintf('%s.%s', $op, $this->podName), $t->result()); return $results; } @@ -412,14 +356,11 @@ public function getCount(array $query, $groupBy = null, $ttl = null) * Selects $fields from the result set determined by $query. * Returns an array of all results, each array element is a CBD graph, keyed by r. * - * @param array $fields array of fields, in the same format as prescribed by MongoPHP - * @param int|null $limit - * @param int $offset - * @param string|null $context + * @param array $fields array of fields, in the same format as prescribed by MongoPHP * - * @return array + * @return array> */ - public function select(array $query, array $fields, ?array $sortBy = null, $limit = null, $offset = 0, $context = null) + public function select(array $query, array $fields, ?array $sortBy = null, ?int $limit = null, ?int $offset = 0, ?string $context = null): array { $t = new Timer(); $t->start(); @@ -453,17 +394,19 @@ public function select(array $query, array $fields, ?array $sortBy = null, $limi 'projection' => $fields, ]; if (!empty($limit)) { - $findOptions['skip'] = (int) $offset; - $findOptions['limit'] = (int) $limit; + $findOptions['skip'] = $offset ?? 0; + $findOptions['limit'] = $limit; } + if (isset($sortBy)) { $findOptions['sort'] = $sortBy; } + $results = $this->collection->find($query, $findOptions); $t->stop(); $this->timingLog(MONGO_SELECT, ['duration' => $t->result(), 'query' => $query]); - $this->getStat()->timer(MONGO_SELECT . ".{$this->podName}", $t->result()); + $this->getStat()->timer(MONGO_SELECT . ('.' . $this->podName), $t->result()); $rows = []; $count = $this->collection->count($query); @@ -491,6 +434,7 @@ public function select(array $query, array $fields, ?array $sortBy = null, $limi } } } + $rows[] = $row; } @@ -509,11 +453,9 @@ public function select(array $query, array $fields, ?array $sortBy = null, $limi * * @deprecated use getGraph * - * @param $query array - * * @return MongoGraph */ - public function describe($query) + public function describe(array $query): ExtendedGraph { return $this->fetchGraph($query, MONGO_DESCRIBE_WITH_CONDITION); } @@ -529,20 +471,15 @@ public function describe($query) * * @return MongoGraph */ - public function graph(array $filter, array $includeProperties = []) + public function graph(array $filter, array $includeProperties = []): ExtendedGraph { return $this->fetchGraph($filter, MONGO_GET_GRAPH, null, $includeProperties); } /** - * Retuns the eTag of the $resource, useful for cache control or optimistic concurrency control. - * - * @param string $resource - * @param string|null $context - * - * @return string + * Returns the eTag of the $resource, useful for cache control or optimistic concurrency control. */ - public function getETag($resource, $context = null) + public function getETag(string $resource, ?string $context = null): string { $this->getStat()->increment(MONGO_GET_ETAG); $resource = $this->labeller->uri_to_alias($resource); @@ -556,26 +493,18 @@ public function getETag($resource, $context = null) /** @var UTCDateTime|null $lastUpdatedDate */ $lastUpdatedDate = $doc[_UPDATED_TS] ?? null; - if ($lastUpdatedDate === null) { - $eTag = ''; - } else { - // PHP 5.3 used MongoDate::__toString() to generate the etag. - // This is incompatible with UTCDate::__toString() so we convert it into a microtime representation. - // This ensures that if it is required to dual run 2 PHP versions, there are no etag compatibility issues. - // Note that MongoDate doesn't go to 8 decimal place precision but still returns it so we go to 6 and pad - // with an extra 2 - $seconds = $lastUpdatedDate->__toString() / 1000; - $eTag = str_pad(number_format($seconds - floor($seconds), 6), 10, '0', STR_PAD_RIGHT) . ' ' . floor($seconds); + return ''; } - return $eTag; + $milliseconds = (int) $lastUpdatedDate->__toString(); + $seconds = intdiv($milliseconds, 1000); + $fraction = ($milliseconds % 1000) / 1000; + + return sprintf('%.8f %d', $fraction, $seconds); } - /** - * @return Views - */ - public function getTripodViews() + public function getTripodViews(): Views { if ($this->tripod_views == null) { $this->tripod_views = new Views( @@ -590,10 +519,7 @@ public function getTripodViews() return $this->tripod_views; } - /** - * @return Tables - */ - public function getTripodTables() + public function getTripodTables(): Tables { if ($this->tripod_tables == null) { $this->tripod_tables = new Tables( @@ -608,19 +534,16 @@ public function getTripodTables() return $this->tripod_tables; } - /** - * @return SearchIndexer - */ - public function getSearchIndexer() + public function getSearchIndexer(): SearchIndexer { - if ($this->search_indexer == null) { - $this->search_indexer = new SearchIndexer($this, $this->readPreference); + if ($this->searchIndexer == null) { + $this->searchIndexer = new SearchIndexer($this, $this->readPreference); } - return $this->search_indexer; + return $this->searchIndexer; } - public function setTransactionLog(TransactionLog $transactionLog) + public function setTransactionLog(TransactionLog $transactionLog): void { $this->getDataUpdater()->setTransactionLog($transactionLog); } @@ -631,10 +554,8 @@ public function setTransactionLog(TransactionLog $transactionLog) * * @param string|null $fromDate only transactions after this specified date. This must be a datetime string i.e. '2010-01-15 00:00:00' * @param string|null $toDate only transactions before this specified date. This must be a datetime string i.e. '2010-01-15 00:00:00' - * - * @return bool */ - public function replayTransactionLog($fromDate = null, $toDate = null) + public function replayTransactionLog(?string $fromDate = null, ?string $toDate = null): bool { return $this->getDataUpdater()->replayTransactionLog($fromDate, $toDate); } @@ -642,11 +563,9 @@ public function replayTransactionLog($fromDate = null, $toDate = null) /** * Register an event hook, which will be executed when the event fires. * - * @param string $eventType - * * @throws Exception when an unrecognised event type is given */ - public function registerHook($eventType, IEventHook $hook) + public function registerHook(string $eventType, IEventHook $hook): void { switch ($eventType) { case IEventHook::EVENT_SAVE_CHANGES: @@ -655,7 +574,7 @@ public function registerHook($eventType, IEventHook $hook) break; default: - throw new Exception("Unrecognised type {$eventType} whilst registering event hook"); + throw new Exception(sprintf('Unrecognised type %s whilst registering event hook', $eventType)); } } @@ -664,11 +583,11 @@ public function registerHook($eventType, IEventHook $hook) * * @param $operation string must be either OP_VIEWS, OP_TABLES or OP_SEARCH * - * @return IComposite + * @return SearchIndexer|Tables|Views * * @throws Exception when an unsupported operation is requested */ - public function getComposite($operation) + public function getComposite(string $operation) { switch ($operation) { case OP_VIEWS: @@ -681,29 +600,25 @@ public function getComposite($operation) return $this->getSearchIndexer(); default: - throw new Exception("Undefined operation '{$operation}' requested"); + throw new Exception(sprintf("Undefined operation '%s' requested", $operation)); } } /** * For mocking. - * - * @return Labeller */ - protected function getLabeller() + protected function getLabeller(): Labeller { return new Labeller(); } /** * Returns the delegate object for saving data in Mongo. - * - * @return Updates */ - protected function getDataUpdater() + protected function getDataUpdater(): Updates { - if (!isset($this->dataUpdater)) { - $readPreference = $this->collection->__debugInfo()['readPreference']->getMode(); + if ($this->updates === null) { + $readPreference = $this->collection->getReadPreference()->getMode(); $opts = [ 'defaultContext' => $this->defaultContext, @@ -714,9 +629,9 @@ protected function getDataUpdater() 'statsConfig' => $this->statsConfig, ]; - $this->dataUpdater = new Updates($this, $opts); + $this->updates = new Updates($this, $opts); } - return $this->dataUpdater; + return $this->updates; } } diff --git a/src/mongo/IComposite.php b/src/mongo/IComposite.php index 453c258a..b7d86a7e 100644 --- a/src/mongo/IComposite.php +++ b/src/mongo/IComposite.php @@ -1,5 +1,7 @@ */ - public function getNamespaces(); + public function getNamespaces(): array; /** * Getter for transaction log connection config. - * - * @return array */ - public function getTransactionLogConfig(); + public function getTransactionLogConfig(): array; /** * @param string $storeName Store name * - * @return string|null + * @return class-string|null */ - public function getSearchProviderClassName($storeName); + public function getSearchProviderClassName(string $storeName): ?string; /** * @param string $storeName Store (database) name * @param string|null $dataSource Database server identifier - * @param string $readPreference Mongo read preference - * - * @return Database + * @param int|string $readPreference Mongo read preference * * @throws ConfigException */ - public function getDatabase($storeName, $dataSource = null, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED); + public function getDatabase(string $storeName, ?string $dataSource = null, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Database; /** - * @param string $storeName Store (database) name - * @param string $podName Pod (collection) name - * @param string $readPreference Mongo read preference - * - * @return Collection + * @param string $storeName Store (database) name + * @param string $podName Pod (collection) name + * @param int|string $readPreference Mongo read preference * * @throws ConfigException */ - public function getCollectionForCBD($storeName, $podName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED); + public function getCollectionForCBD(string $storeName, string $podName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection; /** - * @param string $storeName Store (database) name - * @param string $viewId View spec ID - * @param string $readPreference Mongo read preference - * - * @return Collection + * @param string $storeName Store (database) name + * @param string $viewId View spec ID + * @param int|string $readPreference Mongo read preference * * @throws ConfigException */ - public function getCollectionForView($storeName, $viewId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED); + public function getCollectionForView(string $storeName, string $viewId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection; /** - * @param string $storeName Store (database) name - * @param string $searchDocumentId Search document spec ID - * @param string $readPreference Mongo read preference - * - * @return Collection + * @param string $storeName Store (database) name + * @param string $searchDocumentId Search document spec ID + * @param int|string $readPreference Mongo read preference * * @throws ConfigException */ public function getCollectionForSearchDocument( - $storeName, - $searchDocumentId, + string $storeName, + string $searchDocumentId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED - ); + ): Collection; /** - * @param string $storeName Store (database) name - * @param string $tableId Table spec ID - * @param string $readPreference Mongo read preference - * - * @return Collection + * @param string $storeName Store (database) name + * @param string $tableId Table spec ID + * @param int|string $readPreference Mongo read preference * * @throws ConfigException */ - public function getCollectionForTable($storeName, $tableId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED); + public function getCollectionForTable(string $storeName, string $tableId, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection; /** - * @param string $storeName Store (database) name - * @param array $tables Array of table spec IDs - * @param string $readPreference Mongo read preference + * @param string $storeName Store (database) name + * @param array $tables Array of table spec IDs + * @param int|string $readPreference Mongo read preference * * @return Collection[] * * @throws ConfigException */ public function getCollectionsForTables( - $storeName, + string $storeName, array $tables = [], $readPreference = ReadPreference::RP_PRIMARY_PREFERRED - ); + ): array; /** - * @param string $storeName Store (database) name - * @param array $views Array of view spec IDs - * @param string $readPreference Mongo read preference + * @param string $storeName Store (database) name + * @param array $views Array of view spec IDs + * @param int|string $readPreference Mongo read preference * * @return Collection[] * * @throws ConfigException */ public function getCollectionsForViews( - $storeName, + string $storeName, array $views = [], $readPreference = ReadPreference::RP_PRIMARY_PREFERRED - ); + ): array; /** - * @param string $storeName Store (database) name - * @param array $searchSpecIds Array of search document spec IDs - * @param string $readPreference Mongo read preference + * @param string $storeName Store (database) name + * @param array $searchSpecIds Array of search document spec IDs + * @param int|string $readPreference Mongo read preference * * @return Collection[] * * @throws ConfigException */ public function getCollectionsForSearch( - $storeName, + string $storeName, array $searchSpecIds = [], $readPreference = ReadPreference::RP_PRIMARY_PREFERRED - ); + ): array; /** - * @param string $storeName Store (database) name - * @param string $readPreference Mongo read preference - * - * @return Collection + * @param string $storeName Store (database) name + * @param int|string $readPreference Mongo read preference */ - public function getCollectionForTTLCache($storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED); + public function getCollectionForTTLCache(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection; /** - * @param string $storeName - * @param string $readPreference - * - * @return Collection + * @param int|string $readPreference */ - public function getCollectionForLocks($storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED); + public function getCollectionForLocks(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection; /** - * @param string $storeName Store (database) name - * @param string $readPreference Mongo read preference - * - * @return Collection + * @param string $storeName Store (database) name + * @param int|string $readPreference Mongo read preference */ public function getCollectionForManualRollbackAudit( - $storeName, + string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED - ); + ): Collection; /** - * @param string $storeName Store (database) name - * @param string $readPreference Mongo read preference - * - * @return Collection + * @param string $storeName Store (database) name + * @param int|string $readPreference Mongo read preference */ - public function getCollectionForJobGroups($storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED); + public function getCollectionForJobGroups(string $storeName, $readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Collection; /** - * @param $readPreference Mongo read preference - * - * @return Database + * @param int|string $readPreference Mongo read preference * * @throws ConfigException */ - public function getTransactionLogDatabase($readPreference = ReadPreference::RP_PRIMARY_PREFERRED); + public function getTransactionLogDatabase($readPreference = ReadPreference::RP_PRIMARY_PREFERRED): Database; /** * Return the maximum batch size for async operations. * * @param string $operation Async operation, e.g. OP_TABLES, OP_VIEWS - * - * @return int */ - public function getBatchSize($operation); + public function getBatchSize(string $operation): int; - /** - * @return string - */ - public static function getDiscoverQueueName(); + public static function getDiscoverQueueName(): string; - /** - * @return string - */ - public static function getApplyQueueName(); + public static function getApplyQueueName(): string; - /** - * @return string - */ - public static function getEnsureIndexesQueueName(); + public static function getEnsureIndexesQueueName(): string; - /** - * @return string - */ - public static function getResqueServer(); + public static function getResqueServer(): string; - /** - * @return LoggerInterface - */ - public static function getLogger(); + public static function getLogger(): LoggerInterface; } diff --git a/src/mongo/ImpactedSubject.php b/src/mongo/ImpactedSubject.php index cfc1902a..a4a00044 100644 --- a/src/mongo/ImpactedSubject.php +++ b/src/mongo/ImpactedSubject.php @@ -1,5 +1,7 @@ operation = $operation; } else { - throw new Exception("Invalid operation: {$operation}"); + throw new Exception('Invalid operation: ' . $operation); } $this->storeName = $storeName; $this->podName = $podName; $this->specTypes = $specTypes; - if ($stat) { - $this->stat = $stat; + if ($stat instanceof ITripodStat) { + $this->tripodStat = $stat; } } - /** - * @return string - */ - public function getOperation() + public function getOperation(): ?string { return $this->operation; } - /** - * @return string - */ - public function getPodName() + public function getPodName(): string { return $this->podName; } /** - * @return array + * @return mixed[] */ - public function getResourceId() + public function getResourceId(): array { return $this->resourceId; } /** - * @return array + * @return mixed[] */ - public function getSpecTypes() + public function getSpecTypes(): array { return $this->specTypes; } - /** - * @return string - */ - public function getStoreName() + public function getStoreName(): string { return $this->storeName; } @@ -115,9 +94,9 @@ public function getStoreName() /** * Serialises the data as an array. * - * @return array + * @return array */ - public function toArray() + public function toArray(): array { return [ 'resourceId' => $this->resourceId, @@ -131,21 +110,20 @@ public function toArray() /** * Perform the update on the composite defined by the operation. */ - public function update() + public function update(): void { $tripod = $this->getTripod(); - if (isset($this->stat)) { - $tripod->setStat($this->stat); + if ($this->tripodStat !== null) { + $tripod->setStat($this->tripodStat); } + $tripod->getComposite($this->operation)->update($this); } /** * For mocking. - * - * @return Driver */ - protected function getTripod() + protected function getTripod(): Driver { return new Driver($this->getPodName(), $this->getStoreName(), [ 'readPreference' => ReadPreference::RP_PRIMARY, diff --git a/src/mongo/JobGroup.php b/src/mongo/JobGroup.php index 81b1ade8..e24453ce 100644 --- a/src/mongo/JobGroup.php +++ b/src/mongo/JobGroup.php @@ -1,5 +1,7 @@ storeName = $storeName; if (!$groupId) { @@ -27,6 +31,7 @@ public function __construct($storeName, $groupId = null) } elseif (!$groupId instanceof ObjectId) { $groupId = new ObjectId($groupId); } + $this->id = $groupId; } @@ -35,7 +40,7 @@ public function __construct($storeName, $groupId = null) * * @param int $count Number of jobs in group */ - public function setJobCount($count) + public function setJobCount(int $count): void { $this->getMongoCollection()->updateOne( ['_id' => $this->getId()], @@ -51,7 +56,7 @@ public function setJobCount($count) * * @return int Updated job count */ - public function incrementJobCount($inc = 1) + public function incrementJobCount(int $inc = 1): int { $updateResult = $this->getMongoCollection()->findOneAndUpdate( ['_id' => $this->getId()], @@ -61,27 +66,21 @@ public function incrementJobCount($inc = 1) if (\is_array($updateResult)) { return $updateResult['count']; } - if (isset($updateResult->count)) { - return $updateResult->count; - } + + return $updateResult->count; } - /** - * @return ObjectId - */ - public function getId() + public function getId(): ObjectId { return $this->id; } /** * For mocking. - * - * @return Collection */ - protected function getMongoCollection() + protected function getMongoCollection(): Collection { - if (!isset($this->collection)) { + if ($this->collection === null) { $config = Config::getInstance(); $this->collection = $config->getCollectionForJobGroups($this->storeName); diff --git a/src/mongo/Labeller.php b/src/mongo/Labeller.php index 1fd4a679..d34f3e7c 100644 --- a/src/mongo/Labeller.php +++ b/src/mongo/Labeller.php @@ -1,5 +1,7 @@ uri_to_qname($uri); @@ -45,29 +43,21 @@ public function uri_to_alias($uri) /** * If labeller can generate a uri for this qname, it will return it. Otherwise just returns the original qname. - * - * @param string $qname - * - * @return string */ - public function qname_to_alias($qname) + public function qname_to_alias(?string $qName): ?string { try { - $retVal = $this->qname_to_uri($qname); + $retVal = $this->qname_to_uri($qName); } catch (LabellerException $e) { } - return (empty($retVal)) ? $qname : $retVal; + return (empty($retVal)) ? $qName : $retVal; } /** - * @param string $qName - * - * @return string - * * @throws LabellerException */ - public function qname_to_uri($qName) + public function qname_to_uri(?string $qName): ?string { $retVal = parent::qname_to_uri($qName); if (empty($retVal)) { @@ -79,16 +69,12 @@ public function qname_to_uri($qName) // overrides the default behaviour of trying to return a ns even if the prefix is not registered - instead, throw exception /** - * @param string $ns - * - * @return string - * * @throws LabellerException */ - public function get_prefix($ns) + public function get_prefix(string $ns): string { - $prefix = array_search($ns, $this->_ns); - if ($prefix != null && $prefix !== false) { + $prefix = array_search($ns, $this->_ns, true); + if ($prefix !== false) { return $prefix; } diff --git a/src/mongo/MongoGraph.php b/src/mongo/MongoGraph.php index 1c3d8f69..736f63f8 100644 --- a/src/mongo/MongoGraph.php +++ b/src/mongo/MongoGraph.php @@ -1,5 +1,7 @@ _labeller->qname_to_alias($docId); $contextAlias = $this->_labeller->uri_to_alias($context); @@ -97,12 +87,9 @@ public function to_tripod_array($docId, $context) /** * Returns a mongo-ready doc for views, which can have multiple graphs in the same doc. * - * @param mixed $docId - * @param mixed $context - * - * @return array + * @return array */ - public function to_tripod_view_array($docId, $context) + public function to_tripod_view_array(string $docId, ?string $context): array { $subjects = $this->get_subjects(); $contextAlias = $this->_labeller->uri_to_alias($context); @@ -122,44 +109,44 @@ public function to_tripod_view_array($docId, $context) } /** - * @param array $tarray + * @param array $tarray * * @throws Exception */ - private function add_tarray_to_index($tarray) + private function add_tarray_to_index(array $tarray): void { $_i = []; $predObjects = []; foreach ($tarray as $key => $value) { - if (empty($key)) { - throw new Exception('The predicate cannot be an empty string'); + if (!$this->isValidResource($key)) { + throw new Exception('The predicate must be a non-empty string, ' . var_export($key, true) . ' given'); } + if ($key[0] != '_') { $predicate = $this->qname_to_uri($key); $graphValueObject = $this->toGraphValueObject($value); // Only add if valid values have been found - if (!empty($graphValueObject)) { + if ($graphValueObject !== []) { $predObjects[$predicate] = $graphValueObject; } } elseif ($key == '_id') { // If the subject is invalid then throw an exception - if (!isset($value['r']) || !$this->isValidResource($value['r'])) { - throw new Exception('The subject cannot be an empty string'); + if (!isset($value[_ID_RESOURCE]) || !$this->isValidResource($value[_ID_RESOURCE])) { + throw new Exception('The subject must be a non-empty string, ' . var_export($value[_ID_RESOURCE], true) . ' given'); } } } + $_i[$this->_labeller->qname_to_alias($tarray['_id'][_ID_RESOURCE])] = $predObjects; - $this->add_json(json_encode($_i)); + $this->_index = $this->merge($this->_index, $_i); } /** * Convert from Tripod value object format (comapct) to ExtendedGraph format (verbose). * - * @param array $mongoValueObject - * - * @return array + * @param array $mongoValueObject */ - private function toGraphValueObject($mongoValueObject) + private function toGraphValueObject(array $mongoValueObject): array { $simpleGraphValueObject = []; @@ -169,7 +156,8 @@ private function toGraphValueObject($mongoValueObject) // single value literal $simpleGraphValueObject[] = [ 'type' => 'literal', - 'value' => $mongoValueObject[VALUE_LITERAL]]; + 'value' => $mongoValueObject[VALUE_LITERAL], + ]; } } elseif (array_key_exists(VALUE_URI, $mongoValueObject)) { // only allow valid values @@ -177,7 +165,8 @@ private function toGraphValueObject($mongoValueObject) // single value uri $simpleGraphValueObject[] = [ 'type' => 'uri', - 'value' => $this->_labeller->qname_to_alias($mongoValueObject[VALUE_URI])]; + 'value' => $this->_labeller->qname_to_alias($mongoValueObject[VALUE_URI]), + ]; } } else { // If we have an array of values @@ -188,16 +177,20 @@ private function toGraphValueObject($mongoValueObject) if (!$this->isValidLiteral($value)) { continue; } + $valueTypeLabel = 'literal'; } else { if (!$this->isValidResource($value)) { continue; } + $valueTypeLabel = 'uri'; } + $simpleGraphValueObject[] = [ 'type' => $valueTypeLabel, - 'value' => ($type == VALUE_URI) ? $this->_labeller->qname_to_alias($value) : $value]; + 'value' => ($type == VALUE_URI) ? $this->_labeller->qname_to_alias($value) : $value, + ]; } } } @@ -209,29 +202,23 @@ private function toGraphValueObject($mongoValueObject) /** * Convert from ExtendedGraph value object format (verbose) to Tripod format (compact). * - * @param array $simpleGraphValueObject - * - * @return array + * @param array $simpleGraphValueObject */ - private function toMongoTripodValueObject($simpleGraphValueObject) + private function toMongoTripodValueObject(array $simpleGraphValueObject): array { $valueTypeProp = ($simpleGraphValueObject['type'] == 'literal') ? VALUE_LITERAL : VALUE_URI; return [ - $valueTypeProp => ($simpleGraphValueObject['type'] == 'literal') ? $simpleGraphValueObject['value'] : $this->_labeller->uri_to_alias($simpleGraphValueObject['value'])]; + $valueTypeProp => ($simpleGraphValueObject['type'] == 'literal') ? $simpleGraphValueObject['value'] : $this->_labeller->uri_to_alias($simpleGraphValueObject['value']), + ]; } - /** - * @param ExtendedGraph|null $graph - * @param string $contextAlias - * - * @return array|null - */ - private function index_to_tarray($graph = null, $contextAlias) + private function index_to_tarray(?ExtendedGraph $graph = null, ?string $contextAlias = null): ?array { if ($graph == null) { $graph = $this; } + $_i = $graph->_index; foreach ($_i as $resource => $predObjects) { @@ -243,13 +230,14 @@ private function index_to_tarray($graph = null, $contextAlias) $doc['_id'] = $id; foreach ($predObjects as $predicate => $objects) { $pQName = $this->uri_to_qname($predicate); - if (count($objects) == 1) { + if (count($objects) === 1) { $doc[$pQName] = $this->toMongoTripodValueObject($objects[0]); } else { $values = []; foreach ($objects as $obj) { $values[] = $this->toMongoTripodValueObject($obj); } + $doc[$pQName] = $values; } } diff --git a/src/mongo/base/CompositeBase.php b/src/mongo/base/CompositeBase.php index e15bef12..30c5ceb0 100644 --- a/src/mongo/base/CompositeBase.php +++ b/src/mongo/base/CompositeBase.php @@ -1,5 +1,7 @@ $resourceAlias, _ID_CONTEXT => $contextAlias]; } + $query = [_ID_KEY => ['$in' => $filter]]; $docs = $this->getCollection()->find( $query, @@ -63,15 +61,16 @@ public function getImpactedSubjects(array $subjectsAndPredicatesOfChange, $conte $currentSubjectProperties = []; if (isset($subjectsAndPredicatesOfChange[$docResource])) { $currentSubjectProperties = $subjectsAndPredicatesOfChange[$docResource]; - } elseif (isset($subjectsToAlias[$docResource], $subjectsAndPredicatesOfChange[$subjectsToAlias[$docResource]]) - ) { + } elseif (isset($subjectsToAlias[$docResource], $subjectsAndPredicatesOfChange[$subjectsToAlias[$docResource]])) { $currentSubjectProperties = $subjectsAndPredicatesOfChange[$subjectsToAlias[$docResource]]; } + foreach ($docTypes as $type) { if ($this->checkIfTypeShouldTriggerOperation($type, $types, $currentSubjectProperties)) { if (!array_key_exists($this->getPodName(), $candidates)) { $candidates[$this->getPodName()] = []; } + if (!array_key_exists($docHash, $candidates[$this->getPodName()])) { $candidates[$this->getPodName()][$docHash] = ['id' => $doc[_ID_KEY]]; } @@ -87,6 +86,7 @@ public function getImpactedSubjects(array $subjectsAndPredicatesOfChange, $conte if (!array_key_exists($spec['from'], $candidates)) { $candidates[$spec['from']] = []; } + $docHash = md5($doc[_ID_KEY][_ID_RESOURCE] . $doc[_ID_KEY][_ID_CONTEXT]); if (!array_key_exists($docHash, $candidates[$spec['from']])) { @@ -97,9 +97,11 @@ public function getImpactedSubjects(array $subjectsAndPredicatesOfChange, $conte ], ]; } + if (!array_key_exists('specTypes', $candidates[$spec['from']][$docHash])) { $candidates[$spec['from']][$docHash]['specTypes'] = []; } + // Save the specification type so we only have to regen resources in that table type if (!in_array($doc[_ID_KEY][_ID_TYPE], $candidates[$spec['from']][$docHash]['specTypes'])) { $candidates[$spec['from']][$docHash]['specTypes'][] = $doc[_ID_KEY][_ID_TYPE]; @@ -121,37 +123,24 @@ public function getImpactedSubjects(array $subjectsAndPredicatesOfChange, $conte /** * Returns an array of the rdf types that will trigger the specification. - * - * @return array */ - abstract public function getTypesInSpecifications(); + abstract public function getTypesInSpecifications(): array; /** - * @param string $contextAlias - * - * @return mixed + * @return mixed[] */ - abstract public function findImpactedComposites(array $resourcesAndPredicates, $contextAlias); + abstract public function findImpactedComposites(array $resourcesAndPredicates, string $contextAlias): array; /** * Returns the specification config. - * - * @param string $storeName - * @param string $specId The specification id - * - * @return array|null */ - abstract public function getSpecification($storeName, $specId); + abstract public function getSpecification(string $storeName, string $specId): ?array; /** * Test if the a particular type appears in the array of types associated with a particular spec and that the changeset * includes rdf:type (or is empty, meaning addition or deletion vs. update). - * - * @param string $rdfType - * - * @return bool */ - protected function checkIfTypeShouldTriggerOperation($rdfType, array $validTypes, array $subjectPredicates) + protected function checkIfTypeShouldTriggerOperation(string $rdfType, array $validTypes, array $subjectPredicates): bool { // We don't know if this is an alias or a fqURI, nor what is in the valid types, necessarily $types = [$rdfType]; @@ -171,17 +160,15 @@ protected function checkIfTypeShouldTriggerOperation($rdfType, array $validTypes $intersectingTypes = array_unique(array_intersect($types, $validTypes)); // If views have a matching type *at all*, the operation is triggered - return !empty($intersectingTypes); + return $intersectingTypes !== []; } /** * For mocking. - * - * @return ApplyOperation */ - protected function getApplyOperation() + protected function getApplyOperation(): ApplyOperation { - if (!isset($this->applyOperation)) { + if ($this->applyOperation === null) { $this->applyOperation = new ApplyOperation(); } @@ -192,22 +179,18 @@ protected function getApplyOperation() * Queues a batch of ImpactedSubjects in a single ApplyOperation job. * * @param ImpactedSubject[] $subjects Array of ImpactedSubjects - * @param string $queueName Queue name + * @param string|null $queueName Queue name * @param array $jobOptions Job options */ - protected function queueApplyJob(array $subjects, $queueName, array $jobOptions) + protected function queueApplyJob(array $subjects, ?string $queueName, array $jobOptions): void { $this->getApplyOperation()->createJob($subjects, $queueName, $jobOptions); } /** * For mocking. - * - * @param string $storeName - * - * @return JobGroup */ - protected function getJobGroup($storeName) + protected function getJobGroup(string $storeName): JobGroup { return new JobGroup($storeName); } diff --git a/src/mongo/base/DriverBase.php b/src/mongo/base/DriverBase.php index 8d2bac49..2f9e5b43 100644 --- a/src/mongo/base/DriverBase.php +++ b/src/mongo/base/DriverBase.php @@ -1,8 +1,8 @@ stat == null) { - $this->setStat($this->getStatFromStatFactory($this->statsConfig)); + $this->setStat($this->getStatFromStatFactory()); } return $this->stat; } - public function setStat(ITripodStat $stat) + public function setStat(ITripodStat $stat): void { // TODO: how do we decouple this and still allow StatsD to know which db we're using? - if ($stat instanceof StatsD) { + if ($stat instanceof StatsD && isset($this->storeName)) { $stat->setPivotValue($this->getStoreName()); } + $this->stat = $stat; } /** * Returns stat object config. - * - * @return array */ - public function getStatsConfig() + public function getStatsConfig(): array { - $stat = $this->getStat(); - if ($stat) { - $statConfig = $stat->getConfig(); - } else { - $statConfig = $this->statsConfig; - } - - return $statConfig; + return $this->getStat()->getConfig(); } - /** - * @return string - */ - public function getStoreName() + public function getStoreName(): string { return $this->storeName; } - /** - * @return string - */ - public function getPodName() + public function getPodName(): string { return $this->podName; } /** - * @param string $type - * @param array|null $params - * * @codeCoverageIgnore */ - public function timingLog($type, $params = null) + public function timingLog(string $type, ?array $params = null): void { $type = '[PID ' . getmypid() . '] ' . $type; $this->log(LogLevel::DEBUG, $type, $params); } /** - * @param array|null $params - * @param mixed $message - * * @codeCoverageIgnore */ - public function infoLog($message, $params = null) + public function infoLog(string $message, ?array $params = null): void { $message = '[PID ' . getmypid() . '] ' . $message; $this->log(LogLevel::INFO, $message, $params); } /** - * @param array|null $params - * @param mixed $message - * * @codeCoverageIgnore */ - public function debugLog($message, $params = null) + public function debugLog(string $message, ?array $params = null): void { $message = '[PID ' . getmypid() . '] ' . $message; $this->log(LogLevel::DEBUG, $message, $params); } /** - * @param string $message - * @param array|null $params - * * @codeCoverageIgnore */ - public function errorLog($message, $params = null) + public function errorLog(string $message, ?array $params = null): void { $message = '[PID ' . getmypid() . '] ' . $message; $this->log(LogLevel::ERROR, $message, $params); } /** - * @param string $message - * @param array|null $params - * * @codeCoverageIgnore */ - public function warningLog($message, $params = null) + public function warningLog(string $message, ?array $params = null): void { $message = '[PID ' . getmypid() . '] ' . $message; $this->log(LogLevel::WARNING, $message, $params); } /** - * @static - * - * @return LoggerInterface - * * @codeCoverageIgnore */ - public static function getLogger() + public static function getLogger(): LoggerInterface { if (self::$logger == null) { $log = new Logger('TRIPOD'); @@ -213,46 +140,20 @@ public static function getLogger() /** * For mocking out the creation of stat objects. - * - * @return ITripodStat */ - protected function getStatFromStatFactory(array $config) + protected function getStatFromStatFactory(): ITripodStat { return TripodStatFactory::create($this->statsConfig); } - /** - * @param int $secs - * - * @return int - */ - protected function getExpirySecFromNow($secs) - { - return time() + $secs; - } - - /** - * @param string|null $context - * - * @return mixed - */ - protected function getContextAlias($context = null) + protected function getContextAlias(?string $context = null): string { $contextAlias = $this->labeller->uri_to_alias((empty($context)) ? $this->defaultContext : $context); return (empty($contextAlias)) ? $this->getConfigInstance()->getDefaultContextAlias() : $contextAlias; } - /** - * @param array $query - * @param string $type - * @param Collection|null $collection - * @param array $includeProperties - * @param int $cursorSize - * - * @return MongoGraph - */ - protected function fetchGraph($query, $type, $collection = null, $includeProperties = [], $cursorSize = 101) + protected function fetchGraph(array $query, string $type, ?Collection $collection = null, ?array $includeProperties = [], int $cursorSize = 101): MongoGraph { $graph = new MongoGraph(); @@ -273,14 +174,13 @@ protected function fetchGraph($query, $type, $collection = null, $includePropert foreach ($includeProperties as $property) { $fields[$this->labeller->uri_to_alias($property)] = true; } + $cursor = $collection->find($query, [ 'projection' => $fields, 'batchSize' => $cursorSize, ]); } - $ttlExpiredResources = false; - $retries = 1; $exception = null; $cursorSuccess = false; @@ -290,16 +190,19 @@ protected function fetchGraph($query, $type, $collection = null, $includePropert foreach ($cursor as $result) { // handle MONGO_VIEWS that have expired due to ttl. These are expired // on read (lazily) rather than on write - if ($this instanceof Views && $type == MONGO_VIEW && array_key_exists(_EXPIRES, $result['value'])) { + if ($this instanceof Views && $type == MONGO_VIEW && isset($result['value'][_EXPIRES])) { // if expires < current date, regenerate view.. + $expires = $result['value'][_EXPIRES]; $currentDate = DateUtil::getMongoDate(); - if ($result['value'][_EXPIRES]->__toString() < $currentDate) { + if ($expires < $currentDate) { // regenerate! $this->generateView($result['_id']['type'], $result['_id']['r']); } } + $graph->add_tripod_array($result); } + $cursorSuccess = true; } catch (\Exception $e) { self::getLogger()->error('CursorException attempt ' . $retries . '. Retrying...:' . $e->getMessage()); @@ -309,28 +212,22 @@ protected function fetchGraph($query, $type, $collection = null, $includePropert } } while ($retries <= Config::CONNECTION_RETRIES && $cursorSuccess === false); - if ($cursorSuccess === false) { - self::getLogger()->error('CursorException failed after ' . $retries . ' attempts (MAX:' . Config::CONNECTION_RETRIES . '): ' . $e->getMessage()); - - throw new \Exception($exception); - } + if ($cursorSuccess === false && $exception !== null) { + self::getLogger()->error('CursorException failed after ' . $retries . ' attempts (MAX:' . Config::CONNECTION_RETRIES . '): ' . $exception->getMessage()); - if ($ttlExpiredResources) { - // generate views and retry... - $this->debugLog('One or more view had exceeded TTL was regenerated - request again...'); - $graph = $this->fetchGraph($query, $type, $collection); + throw $exception; } $t->stop(); $this->timingLog($type, ['duration' => $t->result(), 'query' => $query, 'collection' => $collectionName]); if ($type == MONGO_VIEW) { if (array_key_exists('_id.type', $query)) { - $this->getStat()->timer("{$type}.{$query['_id.type']}", $t->result()); + $this->getStat()->timer(sprintf('%s.%s', $type, $query['_id.type']), $t->result()); } elseif (array_key_exists('_id', $query) && array_key_exists('type', $query['_id'])) { - $this->getStat()->timer("{$type}.{$query['_id']['type']}", $t->result()); + $this->getStat()->timer(sprintf('%s.%s', $type, $query['_id']['type']), $t->result()); } } else { - $this->getStat()->timer("{$type}.{$collectionName}", $t->result()); + $this->getStat()->timer(sprintf('%s.%s', $type, $collectionName), $t->result()); } return $graph; @@ -338,11 +235,8 @@ protected function fetchGraph($query, $type, $collection = null, $includePropert /** * Expands an RDF sequence into proper tripod join clauses. - * - * @param array $joins - * @param array $source */ - protected function expandSequence(&$joins, $source) + protected function expandSequence(array &$joins, array $source): void { if (!empty($joins) && isset($joins['followSequence'])) { // add any rdf:_x style properties in the source to the joins array, @@ -362,6 +256,7 @@ protected function expandSequence(&$joins, $source) } } } + unset($joins['followSequence']); } } @@ -369,12 +264,9 @@ protected function expandSequence(&$joins, $source) /** * Adds an _id object (or array of _id objects) to the target document's impact index. * - * @param array &$target - * @param mixed $buildImpactIndex - * * @throws \InvalidArgumentException */ - protected function addIdToImpactIndex(array $id, &$target, $buildImpactIndex = true) + protected function addIdToImpactIndex(array $id, array &$target, bool $buildImpactIndex = true): void { if ($buildImpactIndex) { if (isset($id[_ID_RESOURCE])) { @@ -383,6 +275,7 @@ protected function addIdToImpactIndex(array $id, &$target, $buildImpactIndex = t if (!isset($target[_IMPACT_INDEX])) { $target[_IMPACT_INDEX] = []; } + if (!in_array($id, $target[_IMPACT_INDEX])) { $target[_IMPACT_INDEX][] = $id; } @@ -391,6 +284,7 @@ protected function addIdToImpactIndex(array $id, &$target, $buildImpactIndex = t if (!isset($i[_ID_RESOURCE])) { throw new \InvalidArgumentException('Invalid id format'); } + $this->addIdToImpactIndex($i, $target); } } @@ -399,20 +293,15 @@ protected function addIdToImpactIndex(array $id, &$target, $buildImpactIndex = t /** * For mocking. - * - * @return IConfigInstance */ - protected function getConfigInstance() + protected function getConfigInstance(): IConfigInstance { return \Tripod\Config::getInstance(); } - /** - * @return Database - */ - protected function getDatabase() + protected function getDatabase(): Database { - if (!isset($this->db)) { + if ($this->db === null) { $this->db = $this->config->getDatabase( $this->storeName, $this->config->getDataSourceForPod($this->storeName, $this->podName), @@ -423,103 +312,20 @@ protected function getDatabase() return $this->db; } - /** - * @return Collection - */ - protected function getCollection() + protected function getCollection(): Collection { - if (!isset($this->collection)) { + if ($this->collection === null) { $this->collection = $this->getDatabase()->selectCollection($this->podName); } return $this->collection; } - protected function applyHooks($fn, $hooks, $args = []) - { - switch ($fn) { - case $this::HOOK_FN_PRE: - case $this::HOOK_FN_SUCCESS: - case $this::HOOK_FN_FAILURE: - break; - - default: - throw new Exception("Invalid hook function {$fn} requested"); - } - foreach ($hooks as $hook) { - try { - // @var $hook IEventHook - $hook->{$fn}($args); - } catch (\Exception $e) { - // don't let rabid hooks stop tripod - static::getLogger()->error('Hook ' . get_class($hook) . " threw exception {$e->getMessage()}, continuing"); - } - } - } - /** - * @param string $level - * @param string $message - * @param array|null $params - * * @codeCoverageIgnore */ - private function log($level, $message, $params) - { - ($params == null) ? self::getLogger()->log($level, $message) : self::getLogger()->log($level, $message, $params); - } -} - -final class NoStat implements ITripodStat -{ - /** - * @var self - */ - public static $instance; - - /** - * @param string $operation - * @param int|number $inc - */ - public function increment($operation, $inc = 1) - { - // do nothing - } - - /** - * @param string $operation - * @param number $duration - */ - public function timer($operation, $duration) - { - // do nothing - } - - /** - * @return array - */ - public function getConfig() - { - return []; - } - - /** - * @return self - */ - public static function getInstance() - { - if (self::$instance == null) { - self::$instance = new NoStat(); - } - - return self::$instance; - } - - /** - * @return NoStat - */ - public static function createFromConfig(array $config = []) + private function log(string $level, string $message, ?array $params): void { - return self::getInstance(); + self::getLogger()->log($level, $message, $params ?: []); } } diff --git a/src/mongo/base/JobBase.php b/src/mongo/base/JobBase.php index 4cda81c4..63af5ac1 100644 --- a/src/mongo/base/JobBase.php +++ b/src/mongo/base/JobBase.php @@ -1,5 +1,7 @@ debugLog( '[JOBID ' . $this->job->payload['id'] . '] ' . get_class($this) . '::perform() start' @@ -67,13 +76,13 @@ public function setUp() } } - public function tearDown() + public function tearDown(): void { // stat time taken to process item, from time it was created (queued) $this->timer->stop(); $this->debugLog( '[JOBID ' . $this->job->payload['id'] . '] ' . get_class($this) - . "::perform() done in {$this->timer->result()}ms" + . sprintf('::perform() done in %sms', $this->timer->result()) ); $this->getStat()->timer($this->getStatTimerSuccessKey(), $this->timer->result()); } @@ -81,14 +90,12 @@ public function tearDown() /** * The main method of the job. */ - abstract public function perform(); + abstract public function perform(): void; /** * Called in every job prior to perform(). - * - * @param \Resque_Job The queued job */ - public static function beforePerform(\Resque_Job $job) + public static function beforePerform(\Resque_Job $job): void { $instance = $job->getInstance(); if (!$instance instanceof self) { @@ -104,7 +111,7 @@ public static function beforePerform(\Resque_Job $job) * @param \Exception|\Throwable $e Exception or Error * @param \Resque_Job $job The failed job */ - public static function onFailure($e, \Resque_Job $job) + public static function onFailure($e, \Resque_Job $job): void { $failedJob = $job->getInstance(); if (!$failedJob instanceof self) { @@ -115,30 +122,9 @@ public static function onFailure($e, \Resque_Job $job) $failedJob->getStat()->increment($failedJob->getStatFailureIncrementKey()); } - /** - * @param string $message Log message - * @param mixed $params Log params - */ - public function debugLog($message, $params = null) - { - parent::debugLog($message, $params); - } - - /** - * @param string $message Log message - * @param mixed $params Log params - */ - public function errorLog($message, $params = null) + public function getStat(): ITripodStat { - parent::errorLog($message, $params); - } - - /** - * @return ITripodStat - */ - public function getStat() - { - if (!isset($this->statsConfig)) { + if ($this->statsConfig === []) { $this->getStatsConfig(); } @@ -147,12 +133,10 @@ public function getStat() /** * Gets the stats config for the job. - * - * @return array */ - public function getStatsConfig() + public function getStatsConfig(): array { - if (empty($this->statsConfig)) { + if ($this->statsConfig === []) { $this->setStatsConfig(); } @@ -161,28 +145,15 @@ public function getStatsConfig() /** * Stat string for successful job timer. - * - * @return string */ - abstract protected function getStatTimerSuccessKey(); + abstract protected function getStatTimerSuccessKey(): string; /** * Stat string for failed job increment. - * - * @return string */ - abstract protected function getStatFailureIncrementKey(); + abstract protected function getStatFailureIncrementKey(): string; - /** - * For mocking. - * - * @param string $storeName - * @param string $podName - * @param array $opts - * - * @return Driver - */ - protected function getTripod($storeName, $podName, $opts = []) + protected function getTripod(string $storeName, string $podName, array $opts = []): Driver { $this->getTripodConfig(); @@ -206,10 +177,8 @@ protected function getTripod($storeName, $podName, $opts = []) /** * Make sure each job considers how to validate its args. - * - * @return array */ - protected function getMandatoryArgs() + protected function getMandatoryArgs(): array { return $this->mandatoryArgs; } @@ -219,23 +188,24 @@ protected function getMandatoryArgs() * * @throws \Exception */ - protected function validateArgs() + protected function validateArgs(): void { $message = null; foreach ($this->getMandatoryArgs() as $arg) { if (!isset($this->args[$arg])) { - $message = "Argument {$arg} was not present in supplied job args for job " . get_class($this); + $message = sprintf('Argument %s was not present in supplied job args for job ', $arg) . get_class($this); $this->errorLog($message); throw new \Exception($message); } } + if ($this->configRequired) { $this->ensureConfig(); } } - protected function ensureConfig() + protected function ensureConfig(): void { if (!isset($this->args[self::TRIPOD_CONFIG_KEY]) && !isset($this->args[self::TRIPOD_CONFIG_GENERATOR])) { $message = 'Argument ' . self::TRIPOD_CONFIG_KEY . ' or ' . self::TRIPOD_CONFIG_GENERATOR @@ -247,86 +217,80 @@ protected function ensureConfig() } /** - * @param string $queueName Queue name - * @param string $class Class name - * @param array $data Job arguments - * @param int $retryAttempts If queue fails, retry x times before throwing an exception + * @param string $queueName Queue name + * @param string $class Class name + * @param array $data Job arguments + * @param int $retryAttempts If queue fails, retry x times before throwing an exception * * @return string A tracking token for the submitted job * * @throws JobException If there is a problem queuing the job */ - protected function submitJob($queueName, $class, array $data, $retryAttempts = 5) + protected function submitJob(string $queueName, string $class, array $data, int $retryAttempts = 5): string { // @see https://github.com/chrisboulton/php-resque/issues/228, when this PR is merged we can stop tracking the status in this way try { if (isset($data[self::TRIPOD_CONFIG_GENERATOR]) && $data[self::TRIPOD_CONFIG_GENERATOR]) { $data[self::TRIPOD_CONFIG_GENERATOR] = $this->serializeConfig($data[self::TRIPOD_CONFIG_GENERATOR]); } + $token = $this->enqueue($queueName, $class, $data); - if (!$this->getJobStatus($token)) { - $this->errorLog("Could not retrieve status for queued {$class} job - job {$token} failed to {$queueName}"); + if (!$token || !$this->hasJobStatus($token)) { + $this->errorLog(sprintf('Could not retrieve status for queued %s job - job %s failed to %s', $class, $token, $queueName)); - throw new \Exception("Could not retrieve status for queued job - job {$token} failed to {$queueName}"); + throw new \Exception(sprintf('Could not retrieve status for queued job - job %s failed to %s', $token, $queueName)); } - $this->debugLog("Queued {$class} job with {$token} to {$queueName}"); + + $this->debugLog(sprintf('Queued %s job with %s to %s', $class, $token, $queueName)); return $token; } catch (\Exception $e) { if ($retryAttempts > 0) { sleep(1); // back off for 1 sec - $this->warningLog("Exception queuing {$class} job - {$e->getMessage()}, retrying {$retryAttempts} times"); + $this->warningLog(sprintf('Exception queuing %s job - %s, retrying %d times', $class, $e->getMessage(), $retryAttempts)); return $this->submitJob($queueName, $class, $data, --$retryAttempts); } - $this->errorLog("Exception queuing {$class} job - {$e->getMessage()}"); - throw new JobException("Exception queuing job - {$e->getMessage()}", $e->getCode(), $e); + $this->errorLog(sprintf('Exception queuing %s job - %s', $class, $e->getMessage())); + + throw new JobException('Exception queuing job - ' . $e->getMessage(), $e->getCode(), $e); } } /** * Actually enqueues the job with Resque. Returns a tracking token. For mocking. * - * @param string $queueName - * @param string $class - * @param mixed $data + * @param array $data * - * @internal param bool|\Tripod\Mongo\Jobs\false $tracking - * - * @return string + * @return false|string */ - protected function enqueue($queueName, $class, $data) + protected function enqueue(string $queueName, string $class, array $data) { return \Resque::enqueue($queueName, $class, $data, true); } /** - * Given a token, return the job status. For mocking. - * - * @param string $token - * - * @return mixed + * Given a token, return the job status. */ - protected function getJobStatus($token) + protected function hasJobStatus(string $token): bool { $status = new \Resque_Job_Status($token); - return $status->get(); + return !empty($status->get()); } /** * Take a Tripod Config Serializer and return a config array. * - * @param array|ITripodConfigSerializer $configSerializer An object that implements ITripodConfigSerializer - * - * @return array + * @param mixed $configSerializer An object that implements ITripodConfigSerializer */ - protected function serializeConfig($configSerializer) + protected function serializeConfig($configSerializer): array { if ($configSerializer instanceof ITripodConfigSerializer) { return $configSerializer->serialize(); } + if (is_array($configSerializer)) { return $configSerializer; } @@ -340,10 +304,8 @@ protected function serializeConfig($configSerializer) * Deserialize a tripodConfigGenerator argument to a Tripod Config object. * * @param array $config The serialized Tripod config - * - * @return IConfigInstance */ - protected function deserializeConfig(array $config) + protected function deserializeConfig(array $config): IConfigInstance { Config::setConfig($config); @@ -353,24 +315,23 @@ protected function deserializeConfig(array $config) /** * Sets the Tripod config for the job. */ - protected function setTripodConfig() + protected function setTripodConfig(): void { if (isset($this->args[self::TRIPOD_CONFIG_GENERATOR])) { $config = $this->args[self::TRIPOD_CONFIG_GENERATOR]; } else { $config = $this->args[self::TRIPOD_CONFIG_KEY]; } + $this->tripodConfig = $this->deserializeConfig($config); } /** * Returns the Tripod config required by the job. - * - * @return IConfigInstance */ - protected function getTripodConfig() + protected function getTripodConfig(): IConfigInstance { - if (!isset($this->tripodConfig)) { + if ($this->tripodConfig === null) { $this->ensureConfig(); $this->setTripodConfig(); } @@ -378,22 +339,12 @@ protected function getTripodConfig() return $this->tripodConfig; } - /** - * Sets the stats config for the job. - */ - protected function setStatsConfig() - { - if (isset($this->args['statsConfig'])) { - $this->statsConfig = $this->args['statsConfig']; - } - } - /** * Tripod options to pass between jobs. * - * @return array + * @return array */ - protected function getTripodOptions() + protected function getTripodOptions(): array { $statsConfig = $this->getStatsConfig(); $options = []; @@ -407,17 +358,14 @@ protected function getTripodOptions() /** * Convenience method to pass config to job data. * - * @return array + * @return array */ - protected function generateConfigJobArgs() + protected function generateConfigJobArgs(): array { $configInstance = $this->getConfigInstance(); $args = []; - if ($configInstance instanceof ITripodConfigSerializer) { - $config = $configInstance->serialize(); - } else { - $config = Config::getConfig(); - } + $config = $configInstance->serialize(); + if (isset($config['class'])) { $args[self::TRIPOD_CONFIG_GENERATOR] = $config; } else { @@ -426,4 +374,14 @@ protected function generateConfigJobArgs() return $args; } + + /** + * Sets the stats config for the job. + */ + private function setStatsConfig(): void + { + if (isset($this->args['statsConfig'])) { + $this->statsConfig = $this->args['statsConfig']; + } + } } diff --git a/src/mongo/delegates/SearchDocuments.php b/src/mongo/delegates/SearchDocuments.php index 68b3d451..c91784fe 100644 --- a/src/mongo/delegates/SearchDocuments.php +++ b/src/mongo/delegates/SearchDocuments.php @@ -1,5 +1,7 @@ labeller = new Labeller(); $this->storeName = $storeName; @@ -30,35 +29,26 @@ public function __construct($storeName, Collection $collection, $defaultContext, } /** - * @param string $specId - * @param string $resource - * @param string $context - * - * @return array|null - * * @throws \Exception */ - public function generateSearchDocumentBasedOnSpecId($specId, $resource, $context) + public function generateSearchDocumentBasedOnSpecId(string $specId, ?string $resource, ?string $context): ?array { if (empty($resource)) { throw new \Exception('Resource must be specified'); } + if (empty($context)) { throw new \Exception('Context must be specified'); } $searchSpec = $this->getSearchDocumentSpecification($specId); if (empty($searchSpec)) { - $this->debugLog("Could not find Search Document Specification for {$specId}"); + $this->debugLog('Could not find Search Document Specification for ' . $specId); return null; } - if (isset($searchSpec['from'])) { - $from = $searchSpec['from']; - } else { - $from = $this->podName; - } + $from = $searchSpec['from'] ?? $this->podName; // work out whether or not to index at all $proceedWithGeneration = false; @@ -66,11 +56,12 @@ public function generateSearchDocumentBasedOnSpecId($specId, $resource, $context foreach ($searchSpec['filter'] as $indexRules) { // run a query to work out if (!empty($indexRules['condition'])) { - $irFrom = (!empty($indexRules['from'])) ? $indexRules['from'] : $this->podName; + $irFrom = (empty($indexRules['from'])) ? $this->podName : $indexRules['from']; // add id of current record to rules.. $indexRules['condition']['_id'] = [ 'r' => $this->labeller->uri_to_alias($resource), - 'c' => $this->labeller->uri_to_alias($context)]; + 'c' => $this->labeller->uri_to_alias($context), + ]; if ($this->getConfigInstance()->getCollectionForCBD($this->storeName, $irFrom)->findOne($indexRules['condition'])) { // match found, add this spec id to those that should be generated @@ -82,8 +73,8 @@ public function generateSearchDocumentBasedOnSpecId($specId, $resource, $context } } - if ($proceedWithGeneration == false) { - $this->debugLog("Unable to proceed with generating {$specId} search document for {$resource}, does not satisfy rules"); + if ($proceedWithGeneration === false) { + $this->debugLog(sprintf('Unable to proceed with generating %s search document for %s, does not satisfy rules', $specId, $resource)); return null; } @@ -96,12 +87,12 @@ public function generateSearchDocumentBasedOnSpecId($specId, $resource, $context $sourceDocument = $this->getConfigInstance()->getCollectionForCBD($this->storeName, $from)->findOne(['_id' => $_id]); if (empty($sourceDocument)) { - $this->debugLog("Source document not found for {$resource}, cannot proceed generating {$specId} search document"); + $this->debugLog(sprintf('Source document not found for %s, cannot proceed generating %s search document', $resource, $specId)); return null; } - $this->debugLog("Processing {$specId}"); + $this->debugLog('Processing ' . $specId); // build the document $generatedDocument = [\_CREATED_TS => DateUtil::getMongoDate()]; @@ -113,9 +104,11 @@ public function generateSearchDocumentBasedOnSpecId($specId, $resource, $context if (isset($searchSpec['fields'])) { $this->addFields($sourceDocument, $searchSpec['fields'], $generatedDocument); } + if (isset($searchSpec['indices'])) { $this->addFields($sourceDocument, $searchSpec['indices'], $generatedDocument, true); } + if (isset($searchSpec['joins'])) { $this->doJoin($sourceDocument, $searchSpec['joins'], $generatedDocument, $from); } @@ -123,23 +116,8 @@ public function generateSearchDocumentBasedOnSpecId($specId, $resource, $context return $generatedDocument; } - /** - * @param string $resource - * @param string $context - * - * @return array - * - * @throws \Exception - */ - public function generateSearchDocumentsBasedOnRdfTypes(array $rdfTypes, $resource, $context) + public function generateSearchDocumentsBasedOnRdfTypes(array $rdfTypes, string $resource, string $context): array { - if (empty($resource)) { - throw new \Exception('Resource must be specified'); - } - if (empty($context)) { - throw new \Exception('Context must be specified'); - } - // this is what is returned $generatedSearchDocuments = []; @@ -157,27 +135,19 @@ public function generateSearchDocumentsBasedOnRdfTypes(array $rdfTypes, $resourc $generatedSearchDocuments[] = $this->generateSearchDocumentBasedOnSpecId($searchSpec['_id'], $resource, $context); } } + $timer->stop(); // echo "\n\tTook " . $timer->result() . " ms to generate search documents\n"; return $generatedSearchDocuments; } - /** - * @return string - */ - public function getSearchCollectionName() + public function getSearchCollectionName(): string { return SEARCH_INDEX_COLLECTION; } - /** - * @param array $source - * @param array $joins - * @param array $target - * @param string $from - */ - protected function doJoin($source, $joins, &$target, $from) + protected function doJoin(array $source, array $joins, array &$target, string $from): void { // expand sequences before proceeding $this->expandSequence($joins, $source); @@ -231,10 +201,7 @@ protected function doJoin($source, $joins, &$target, $from) } } - /** - * @param bool $isIndex - */ - protected function addFields(array $source, array $fieldsOrIndices, array &$target, $isIndex = false) + protected function addFields(array $source, array $fieldsOrIndices, array &$target, bool $isIndex = false): void { foreach ($fieldsOrIndices as $f) { if (isset($f['predicates'])) { @@ -257,6 +224,7 @@ protected function addFields(array $source, array $fieldsOrIndices, array &$targ } } } + // now add the values $this->addValuesToTarget($values, $f, $target); } @@ -269,6 +237,7 @@ protected function addFields(array $source, array $fieldsOrIndices, array &$targ if ($f['value'] == '_link_') { $this->warningLog("Search spec value '_link_' is deprecated", $f); } + $values[] = $this->labeller->qname_to_alias($source['_id']['r']); } @@ -277,22 +246,16 @@ protected function addFields(array $source, array $fieldsOrIndices, array &$targ } } - /** - * @param mixed $specId - * - * @return array|null - */ - protected function getSearchDocumentSpecification($specId) + protected function getSearchDocumentSpecification(string $specId): ?array { return $this->getConfigInstance()->getSearchDocumentSpecification($this->storeName, $specId); } /** - * @param array $values - * @param array $field - * @param array $target + * @param array $field + * @param array $values */ - private function addValuesToTarget($values, $field, &$target) + private function addValuesToTarget(array $values, array $field, array &$target): void { $objName = null; $name = $field['fieldName']; @@ -301,19 +264,14 @@ private function addValuesToTarget($values, $field, &$target) $parts = explode('.', $name); $objName = $parts[0]; $name = $parts[1]; - } // todo: if theres more than 2 parts throw error - - $limit = null; - if (isset($field['limit'])) { - $limit = $field['limit']; - } else { - $limit = count($values); } - if (count($values) > 0) { + $limit = $field['limit'] ?? count($values); + + if ($values !== []) { for ($i = 0; $i < $limit; $i++) { $v = $values[$i]; - if (empty($objName)) { + if (in_array($objName, [null, '', '0'], true)) { if (!isset($target[$name])) { $target[$name] = $v; } elseif (is_array($target[$name])) { @@ -324,17 +282,15 @@ private function addValuesToTarget($values, $field, &$target) $target[$name][] = $existingVal; $target[$name][] = $v; } + } elseif (!isset($target[$objName][$name])) { + $target[$objName][$name] = $v; + } elseif (is_array($target[$objName][$name])) { + $target[$objName][$name][] = $v; } else { - if (!isset($target[$objName][$name])) { - $target[$objName][$name] = $v; - } elseif (is_array($target[$objName][$name])) { - $target[$objName][$name][] = $v; - } else { - $existingVal = $target[$objName][$name]; - $target[$objName][$name] = []; - $target[$objName][$name][] = $existingVal; - $target[$objName][$name][] = $v; - } + $existingVal = $target[$objName][$name]; + $target[$objName][$name] = []; + $target[$objName][$name][] = $existingVal; + $target[$objName][$name][] = $v; } } } diff --git a/src/mongo/delegates/SearchIndexer.php b/src/mongo/delegates/SearchIndexer.php index 8e9709fd..c1416185 100644 --- a/src/mongo/delegates/SearchIndexer.php +++ b/src/mongo/delegates/SearchIndexer.php @@ -1,5 +1,7 @@ getResourceId(); $resourceUri = $resource[_ID_RESOURCE]; @@ -60,31 +56,20 @@ public function update(ImpactedSubject $subject) ); } - /** - * @return array - */ - public function getTypesInSpecifications() + public function getTypesInSpecifications(): array { return $this->config->getTypesInSearchSpecifications($this->storeName, $this->getPodName()); } /** * Returns the operation this composite can satisfy. - * - * @return string */ - public function getOperationType() + public function getOperationType(): string { return OP_SEARCH; } - /** - * @param string $storeName - * @param string $specId - * - * @return array|null - */ - public function getSpecification($storeName, $specId) + public function getSpecification(string $storeName, string $specId): ?array { return $this->config->getSearchDocumentSpecification($storeName, $specId); } @@ -92,12 +77,9 @@ public function getSpecification($storeName, $specId) /** * Removes all existing documents for the supplied resource and regenerate the search documents. * - * @param string $resourceUri - * @param string $context - * @param string $podName * @param array|string|null $specType */ - public function generateAndIndexSearchDocuments($resourceUri, $context, $podName, $specType = []) + public function generateAndIndexSearchDocuments(string $resourceUri, string $context, string $podName, $specType = []): void { $mongoCollection = $this->config->getCollectionForCBD($this->storeName, $podName); @@ -152,19 +134,14 @@ public function generateAndIndexSearchDocuments($resourceUri, $context, $podName } /** - * @param string $searchDocumentType - * @param string|null $resourceUri - * @param string|null $context - * @param string|null $queueName - * * @return array|null Will return an array with a count and group id, if $queueName is sent and $resourceUri is null */ public function generateSearchDocuments( - $searchDocumentType, - $resourceUri = null, - $context = null, - $queueName = null - ) { + string $searchDocumentType, + ?string $resourceUri = null, + ?string $context = null, + ?string $queueName = null + ): ?array { $t = new Timer(); $t->start(); // default the context @@ -174,26 +151,26 @@ public function generateSearchDocuments( if ($resourceUri) { $this->generateAndIndexSearchDocuments($resourceUri, $contextAlias, $spec['from'], $searchDocumentType); - return; + return null; } // default collection $from = $spec['from'] ?? $this->podName; $types = []; - if (is_array($spec['type'])) { - foreach ($spec['type'] as $type) { - $types[] = ['rdf:type.u' => $this->labeller->qname_to_alias($type)]; - $types[] = ['rdf:type.u' => $this->labeller->uri_to_alias($type)]; + if (isset($spec['type'])) { + if (is_array($spec['type'])) { + foreach ($spec['type'] as $type) { + $types[] = ['rdf:type.u' => $this->labeller->qname_to_alias($type)]; + $types[] = ['rdf:type.u' => $this->labeller->uri_to_alias($type)]; + } + } else { + $types[] = ['rdf:type.u' => $this->labeller->qname_to_alias($spec['type'])]; + $types[] = ['rdf:type.u' => $this->labeller->uri_to_alias($spec['type'])]; } - } else { - $types[] = ['rdf:type.u' => $this->labeller->qname_to_alias($spec['type'])]; - $types[] = ['rdf:type.u' => $this->labeller->uri_to_alias($spec['type'])]; } + $filter = ['$or' => $types]; - if (isset($resource)) { - $filter['_id'] = [_ID_RESOURCE => $this->labeller->uri_to_alias($resource), _ID_CONTEXT => $contextAlias]; - } $count = $this->getConfigInstance()->getCollectionForCBD($this->getStoreName(), $from)->count($filter); $docs = $this->getConfigInstance() @@ -205,14 +182,15 @@ public function generateSearchDocuments( $jobOptions = []; $subjects = []; - if ($queueName && !$resourceUri) { + if ($queueName) { $jobOptions['statsConfig'] = $this->getStatsConfig(); $jobGroup = $this->getJobGroup($this->storeName); $jobOptions[ApplyOperation::TRACKING_KEY] = $jobGroup->getId()->__toString(); $jobGroup->setJobCount($count); } + foreach ($docs as $doc) { - if ($queueName && !$resourceUri) { + if ($queueName) { $subject = new ImpactedSubject( $doc['_id'], OP_SEARCH, @@ -237,17 +215,18 @@ public function generateSearchDocuments( } } - if (!empty($subjects)) { + if ($subjects !== []) { $this->queueApplyJob($subjects, $queueName, $jobOptions); } $t->stop(); $this->timingLog(MONGO_CREATE_TABLE, [ - 'type' => $spec['type'], + 'type' => $spec['type'] ?? null, 'duration' => $t->result(), 'filter' => $filter, - 'from' => $from]); - $this->getStat()->timer(MONGO_CREATE_SEARCH_DOC . ".{$searchDocumentType}", $t->result()); + 'from' => $from, + ]); + $this->getStat()->timer(MONGO_CREATE_SEARCH_DOC . ('.' . $searchDocumentType), $t->result()); $stat = ['count' => $count]; if (isset($jobOptions[ApplyOperation::TRACKING_KEY])) { @@ -258,47 +237,29 @@ public function generateSearchDocuments( } /** - * @param string $context - * - * @return array|mixed + * @return mixed[] */ - public function findImpactedComposites(array $resourcesAndPredicates, $context) + public function findImpactedComposites(array $resourcesAndPredicates, string $contextAlias): array { - return $this->getSearchProvider()->findImpactedDocuments($resourcesAndPredicates, $context); + return $this->getSearchProvider()->findImpactedDocuments($resourcesAndPredicates, $contextAlias); } - /** - * @param string $typeId - * - * @return array|bool - */ - public function deleteSearchDocumentsByTypeId($typeId) + public function deleteSearchDocumentsByTypeId(string $typeId): int { return $this->getSearchProvider()->deleteSearchDocumentsByTypeId($typeId); } - /** - * @return ISearchProvider - */ - protected function getSearchProvider() + protected function getSearchProvider(): ?ISearchProvider { - return $this->configuredProvider; + return $this->searchProvider; } - /** - * @param string $context - * - * @return SearchDocuments - */ - protected function getSearchDocumentGenerator(Collection $collection, $context) + protected function getSearchDocumentGenerator(Collection $collection, string $context): SearchDocuments { return new SearchDocuments($this->storeName, $collection, $context, $this->tripod->getStat()); } - /** - * @return array - */ - protected function deDupe(array $input) + protected function deDupe(array $input): array { $output = []; foreach ($input as $i) { @@ -318,7 +279,7 @@ protected function deDupe(array $input) * * @throws SearchException If provider class cannot be found */ - protected function setSearchProvider(Driver $tripod, ?IConfigInstance $config = null) + protected function setSearchProvider(Driver $tripod, ?IConfigInstance $config = null): void { if (is_null($config)) { $config = $this->getConfigInstance(); @@ -326,10 +287,10 @@ protected function setSearchProvider(Driver $tripod, ?IConfigInstance $config = $provider = $config->getSearchProviderClassName($tripod->getStoreName()); if (class_exists($provider)) { - $this->configuredProvider = new $provider($tripod); + $this->searchProvider = new $provider($tripod); } else { throw new SearchException( - "Did not recognise Search Provider, or could not find class: {$provider}" + 'Did not recognise Search Provider, or could not find class: ' . $provider ); } } diff --git a/src/mongo/delegates/Tables.php b/src/mongo/delegates/Tables.php index ec57c828..3efa6d14 100644 --- a/src/mongo/delegates/Tables.php +++ b/src/mongo/delegates/Tables.php @@ -1,11 +1,14 @@ [ 'glue' => true, 'predicates' => true, @@ -42,50 +43,37 @@ class Tables extends CompositeBase /** * Computed field config - A list of valid functions to write dynamic table row field values. * - * @var array - * * @static */ - public static $computedFieldFunctions = ['conditional', 'replace', 'arithmetic']; + public static array $computedFieldFunctions = ['conditional', 'replace', 'arithmetic']; /** * Computed conditional config - list of allowed conditional operators. * - * @var array - * * @static */ - public static $conditionalOperators = ['>', '<', '>=', '<=', '==', '!=', 'contains', 'not contains', '~=', '!~']; + public static array $conditionalOperators = ['>', '<', '>=', '<=', '==', '!=', 'contains', 'not contains', '~=', '!~']; /** * Computed arithmetic config - list of allowed arithmetic operators. * - * @var array - * * @static */ - public static $arithmeticOperators = ['+', '-', '*', '/', '%']; + public static array $arithmeticOperators = ['+', '-', '*', '/', '%']; - /** - * @var array - */ - protected $temporaryFields = []; + protected array $temporaryFields = []; /** * Construct accepts actual objects rather than strings as this class is a delegate of * Tripod and should inherit connections set up there. * - * @param string $storeName - * @param string $defaultContext - * @param ITripodStat|null $stat - * @param string $readPreference - * todo: MongoCollection -> podName + * @param int|string $readPreference */ public function __construct( - $storeName, + string $storeName, Collection $collection, - $defaultContext, - $stat = null, + ?string $defaultContext, + ?ITripodStat $stat = null, $readPreference = ReadPreference::RP_PRIMARY ) { $this->labeller = new Labeller(); @@ -100,10 +88,8 @@ public function __construct( /** * Receive update from subject. - * - * @param ImpactedSubject */ - public function update(ImpactedSubject $subject) + public function update(ImpactedSubject $subject): void { $resource = $subject->getResourceId(); $resourceUri = $resource[_ID_RESOURCE]; @@ -114,10 +100,8 @@ public function update(ImpactedSubject $subject) /** * Returns an array of the rdf types that will trigger the table specification. - * - * @return array */ - public function getTypesInSpecifications() + public function getTypesInSpecifications(): array { return $this->config->getTypesInTableSpecifications($this->storeName, $this->getPodName()); } @@ -125,11 +109,9 @@ public function getTypesInSpecifications() /** * Returns an array of table rows that are impacted by the changes. * - * @param string $contextAlias - * - * @return array + * @return mixed[] */ - public function findImpactedComposites(array $resourcesAndPredicates, $contextAlias) + public function findImpactedComposites(array $resourcesAndPredicates, string $contextAlias): array { $contextAlias = $this->getContextAlias($contextAlias); // belt and braces @@ -150,7 +132,7 @@ public function findImpactedComposites(array $resourcesAndPredicates, $contextAl $id = [_ID_RESOURCE => $resourceAlias, _ID_CONTEXT => $contextAlias]; // If we don't have a working config or there are no predicates listed, remove all // rows associated with the resource in all tables - if (empty($tablePredicates) || empty($resourcePredicates)) { + if ($tablePredicates === [] || empty($resourcePredicates)) { // build $filter for queries to impact index $resourceFilters[] = $id; } else { @@ -160,6 +142,7 @@ public function findImpactedComposites(array $resourcesAndPredicates, $contextAl if (!isset($tableFilters[$tableType])) { $tableFilters[$tableType] = []; } + // build $filter for queries to impact index $tableFilters[$tableType][] = $id; } @@ -167,7 +150,7 @@ public function findImpactedComposites(array $resourcesAndPredicates, $contextAl } } - if (empty($tableFilters) && !empty($resourceFilters)) { + if ($tableFilters === [] && $resourceFilters !== []) { $query = ['value.' . _IMPACT_INDEX => ['$in' => $resourceFilters]]; } else { $query = []; @@ -176,7 +159,7 @@ public function findImpactedComposites(array $resourcesAndPredicates, $contextAl $query[] = ['value.' . _IMPACT_INDEX => ['$in' => $filters], '_id.' . _ID_TYPE => $tableType]; } - if (!empty($resourceFilters)) { + if ($resourceFilters !== []) { $query[] = ['value.' . _IMPACT_INDEX => ['$in' => $resourceFilters]]; } @@ -187,7 +170,7 @@ public function findImpactedComposites(array $resourcesAndPredicates, $contextAl } } - if (empty($query)) { + if ($query === []) { return []; } @@ -207,23 +190,15 @@ public function findImpactedComposites(array $resourcesAndPredicates, $contextAl return $affectedTableRows; } - /** - * @param string $storeName - * @param string $tableSpecId - * - * @return array|null - */ - public function getSpecification($storeName, $tableSpecId) + public function getSpecification(string $storeName, string $tableSpecId): ?array { return $this->config->getTableSpecification($storeName, $tableSpecId); } /** * Returns the operation this composite can satisfy. - * - * @return string */ - public function getOperationType() + public function getOperationType(): string { return OP_TABLES; } @@ -231,21 +206,19 @@ public function getOperationType() /** * Query the tables collection and return the results. * - * @param string $tableSpecId - * @param int $offset - * @param int $limit - * @param array $options Table query options + * @param array $options Table query options + * @param array $filter * - * @return array + * @return array{head: array{count: int, offset: int, limit: int}, results: array|CursorInterface} */ public function getTableRows( - $tableSpecId, + string $tableSpecId, array $filter = [], - array $sortBy = [], - $offset = 0, - $limit = 10, + ?array $sortBy = [], + ?int $offset = 0, + ?int $limit = 10, array $options = [] - ) { + ): array { $t = new Timer(); $t->start(); @@ -268,20 +241,17 @@ public function getTableRows( $findOptions = []; if (!empty($limit)) { - $findOptions['skip'] = (int) $offset; - $findOptions['limit'] = (int) $limit; + $findOptions['skip'] = $offset ?? 0; + $findOptions['limit'] = $limit; } + if (isset($sortBy)) { $findOptions['sort'] = $sortBy; } $results = $collection->find($filter, $findOptions); - if ($options['includeCount']) { - $count = $collection->count($filter); - } else { - $count = -1; - } + $count = $options['includeCount'] ? $collection->count($filter) : -1; $results->setTypeMap(['root' => $options['documentType'], 'document' => 'array', 'array' => 'array']); @@ -290,7 +260,7 @@ public function getTableRows( MONGO_TABLE_ROWS, ['duration' => $t->result(), 'query' => $filter, 'collection' => TABLE_ROWS_COLLECTION] ); - $this->getStat()->timer(MONGO_TABLE_ROWS . ".{$tableSpecId}", $t->result()); + $this->getStat()->timer(MONGO_TABLE_ROWS . ('.' . $tableSpecId), $t->result()); return [ 'head' => [ @@ -305,12 +275,11 @@ public function getTableRows( /** * Returns the distinct values for a table column, optionally filtered by query. * - * @param string $tableSpecId - * @param string $fieldName + * @param array $filter * - * @return array + * @return array */ - public function distinct($tableSpecId, $fieldName, array $filter = []) + public function distinct(string $tableSpecId, string $fieldName, array $filter = []): array { $t = new Timer(); $t->start(); @@ -323,7 +292,7 @@ public function distinct($tableSpecId, $fieldName, array $filter = []) $t->stop(); $query = ['distinct' => $fieldName, 'filter' => $filter]; $this->timingLog(MONGO_TABLE_ROWS_DISTINCT, ['duration' => $t->result(), 'query' => $query, 'collection' => TABLE_ROWS_COLLECTION]); - $this->getStat()->timer(MONGO_TABLE_ROWS_DISTINCT . ".{$tableSpecId}", $t->result()); + $this->getStat()->timer(MONGO_TABLE_ROWS_DISTINCT . ('.' . $tableSpecId), $t->result()); return [ 'head' => [ @@ -336,20 +305,21 @@ public function distinct($tableSpecId, $fieldName, array $filter = []) /** * This method will delete all table rows where the _id.type matches the specified $tableId. * - * @param string $tableId Table spec ID - * @param UTCDateTime|null $timestamp Optional timestamp to delete all table rows that are older than + * @param string $tableId Table spec ID + * @param int|UTCDateTime|null $timestamp Optional timestamp to delete all table rows that are older than * * @return int The number of table rows deleted */ - public function deleteTableRowsByTableId($tableId, $timestamp = null) + public function deleteTableRowsByTableId(string $tableId, $timestamp = null): int { $t = new Timer(); $t->start(); + $tableSpec = $this->getConfigInstance()->getTableSpecification($this->storeName, $tableId); if ($tableSpec == null) { - $this->debugLog("Could not find a table specification for {$tableId}"); + $this->debugLog('Could not find a table specification for ' . $tableId); - return; + return 0; } $query = ['_id.type' => $tableId]; @@ -357,11 +327,13 @@ public function deleteTableRowsByTableId($tableId, $timestamp = null) if (!$timestamp instanceof UTCDateTime) { $timestamp = DateUtil::getMongoDate($timestamp); } + $query['$or'] = [ [\_CREATED_TS => ['$lt' => $timestamp]], [\_CREATED_TS => ['$exists' => false]], ]; } + $deleteResult = $this->getCollectionForTableSpec($tableId) ->deleteMany($query); @@ -373,15 +345,8 @@ public function deleteTableRowsByTableId($tableId, $timestamp = null) /** * This method finds all the table specs for the given $rdfType and generates the table rows for the $subject one by one. - * - * @param string $rdfType - * @param string|null $subject - * @param string|null $context - * @param array $specTypes - * - * @return mixed */ - public function generateTableRowsForType($rdfType, $subject = null, $context = null, $specTypes = []) + public function generateTableRowsForType(string $rdfType, string $subject, string $context, array $specTypes = []): void { $rdfType = $this->labeller->qname_to_alias($rdfType); $rdfTypeAlias = $this->labeller->uri_to_alias($rdfType); @@ -405,38 +370,36 @@ public function generateTableRowsForType($rdfType, $subject = null, $context = n if (!is_array($types)) { $types = [$types]; } + if (in_array($rdfType, $types) || in_array($rdfTypeAlias, $types)) { $foundSpec = true; - $this->debugLog("Processing {$tableSpec[_ID_KEY]}"); + $this->debugLog('Processing ' . $tableSpec[_ID_KEY]); $this->generateTableRows($key, $subject, $context); } } } + if (!$foundSpec) { - $this->debugLog("Could not find any table specifications for {$subject} with resource type '{$rdfType}'"); + $this->debugLog(sprintf("Could not find any table specifications for %s with resource type '%s'", $subject, $rdfType)); return; } } /** - * @param string $tableType - * @param string|null $resource - * @param string|null $context * @param string|null $queueName Queue for background bulk generation - * - * @return array */ - public function generateTableRows($tableType, $resource = null, $context = null, $queueName = null) + public function generateTableRows(string $tableType, ?string $resource = null, ?string $context = null, ?string $queueName = null): ?array { $t = new Timer(); $t->start(); + $this->temporaryFields = []; $tableSpec = $this->getConfigInstance()->getTableSpecification($this->storeName, $tableType); $collection = $this->getConfigInstance()->getCollectionForTable($this->storeName, $tableType); if (empty($tableSpec)) { - $this->debugLog("Could not find a table specification for {$tableType}"); + $this->debugLog('Could not find a table specification for ' . $tableType); return null; } @@ -457,6 +420,7 @@ public function generateTableRows($tableType, $resource = null, $context = null, $types[] = ['rdf:type.u' => $this->labeller->qname_to_alias($tableSpec['type'])]; $types[] = ['rdf:type.u' => $this->labeller->uri_to_alias($tableSpec['type'])]; } + $filter = ['$or' => $types]; if (isset($resource)) { $filter['_id'] = [ @@ -473,7 +437,7 @@ public function generateTableRows($tableType, $resource = null, $context = null, $jobOptions = []; $subjects = []; - if ($queueName && !$resource && ($this->stat || !empty($this->statsConfig))) { + if ($queueName && !$resource && ($this->stat || $this->statsConfig !== [])) { $jobOptions['statsConfig'] = $this->getStatsConfig(); $jobGroup = $this->getJobGroup($this->storeName); $jobOptions[ApplyOperation::TRACKING_KEY] = $jobGroup->getId()->__toString(); @@ -511,6 +475,7 @@ public function generateTableRows($tableType, $resource = null, $context = null, if (isset($tableSpec['joins'])) { $this->doJoins($doc, $tableSpec['joins'], $value, $from, $contextAlias); } + if (isset($tableSpec['counts'])) { $this->doCounts($doc, $tableSpec['counts'], $value); } @@ -526,7 +491,7 @@ public function generateTableRows($tableType, $resource = null, $context = null, } } - if (!empty($subjects)) { + if ($subjects !== []) { $this->queueApplyJob($subjects, $queueName, $jobOptions); } @@ -535,8 +500,9 @@ public function generateTableRows($tableType, $resource = null, $context = null, 'type' => $tableSpec['type'], 'duration' => $t->result(), 'filter' => $filter, - 'from' => $from]); - $this->getStat()->timer(MONGO_CREATE_TABLE . ".{$tableType}", $t->result()); + 'from' => $from, + ]); + $this->getStat()->timer(MONGO_CREATE_TABLE . ('.' . $tableType), $t->result()); $stat = ['count' => $count]; if (isset($jobOptions[ApplyOperation::TRACKING_KEY])) { @@ -549,12 +515,12 @@ public function generateTableRows($tableType, $resource = null, $context = null, /** * Count the number of documents in the spec that match $filters. * - * @param string $tableSpec Table spec ID - * @param array $filters Query filters to get count on + * @param string $tableSpec Table spec ID + * @param array $filters Query filters to get count on * * @return int */ - public function count($tableSpec, array $filters = []) + public function count(string $tableSpec, array $filters = []) { $filters['_id.type'] = $tableSpec; @@ -566,7 +532,7 @@ public function count($tableSpec, array $filters = []) * @param string|null $context Optional context * @param array|string|null $specType Optional table type or array of table types to delete from */ - protected function deleteTableRowsForResource($resource, $context = null, $specType = null) + protected function deleteTableRowsForResource(string $resource, ?string $context = null, $specType = null): void { $t = new Timer(); $t->start(); @@ -578,15 +544,14 @@ protected function deleteTableRowsForResource($resource, $context = null, $specT $specTypes = $this->config->getTableSpecifications($this->storeName); if (empty($specType)) { $specNames = array_keys($specTypes); - } else { - if (is_string($specType)) { - $query[_ID_KEY][_ID_TYPE] = $specType; - $specNames = [$specType]; - } elseif (is_array($specType)) { - $query[_ID_KEY . '.' . _ID_TYPE] = ['$in' => $specType]; - $specNames = $specType; - } + } elseif (is_string($specType)) { + $query[_ID_KEY][_ID_TYPE] = $specType; + $specNames = [$specType]; + } elseif (is_array($specType)) { + $query[_ID_KEY . '.' . _ID_TYPE] = ['$in' => $specType]; + $specNames = $specType; } + foreach ($specNames as $specName) { // Ignore any other types of specs that might have been passed in here if (isset($specTypes[$specName])) { @@ -601,12 +566,8 @@ protected function deleteTableRowsForResource($resource, $context = null, $specT /** * This method handles invalidation and regeneration of table rows based on impact index, before delegating to * generateTableRowsForType() for re-generation of any table rows for the $resource. - * - * @param string $resource - * @param string|null $context - * @param array $specTypes */ - protected function generateTableRowsForResource($resource, $context = null, $specTypes = []) + protected function generateTableRowsForResource(string $resource, ?string $context = null, array $specTypes = []): void { $resourceAlias = $this->labeller->uri_to_alias($resource); $contextAlias = $this->getContextAlias($context); @@ -646,11 +607,11 @@ protected function generateTableRowsForResource($resource, $context = null, $spe * If an exception in thrown because a field is too large to index, the field is * truncated and the save is retried. * - * @param array $generatedRow the rows to save + * @param array $generatedRow the rows to save * * @throws \Exception */ - protected function truncatingSave(Collection $collection, array $generatedRow) + protected function truncatingSave(Collection $collection, array $generatedRow): void { try { $collection->updateOne(['_id' => $generatedRow['_id']], ['$set' => $generatedRow], ['upsert' => true]); @@ -668,29 +629,26 @@ protected function truncatingSave(Collection $collection, array $generatedRow) /** * Truncate any indexed fields in the generated rows which are too large to index. * - * @param array $generatedRow - Pass by reference so that the contents is truncated + * @param array $generatedRow - Pass by reference so that the contents is truncated */ - protected function truncateFields(Collection $collection, array &$generatedRow) + protected function truncateFields(Collection $collection, array &$generatedRow): void { // Find the name of any indexed fields $indexedFields = []; $indexesGroupedByCollection = $this->config->getIndexesGroupedByCollection($this->storeName); - if (isset($indexesGroupedByCollection, $indexesGroupedByCollection[$collection->getCollectionName()])) { - $indexes = $indexesGroupedByCollection[$collection->getCollectionName()]; - if (isset($indexes)) { - foreach ($indexes as $repset) { - foreach ($repset as $index) { - foreach ($index as $indexedFieldname => $v) { - if (strpos($indexedFieldname, 'value.') === 0) { - $indexedFields[] = substr($indexedFieldname, strlen('value.')); - } + if (isset($indexesGroupedByCollection[$collection->getCollectionName()])) { + foreach ($indexesGroupedByCollection[$collection->getCollectionName()] as $repset) { + foreach ($repset as $index) { + foreach ($index as $indexedFieldname => $v) { + if (strpos($indexedFieldname, 'value.') === 0) { + $indexedFields[] = substr($indexedFieldname, strlen('value.')); } } } } } - if (count($indexedFields) > 0 && isset($generatedRow['value']) && is_array($generatedRow['value'])) { + if ($indexedFields !== [] && isset($generatedRow['value']) && is_array($generatedRow['value'])) { // Iterate over generated rows BY REFERENCE (&) - we are going to modify the contents of $field foreach ($generatedRow as &$field) { foreach ($indexedFields as $indexedFieldname) { @@ -698,21 +656,18 @@ protected function truncateFields(Collection $collection, array &$generatedRow) // Adjust the max key size allowed to take it into account. $maxKeySize = 1020 - strlen('value_' . $indexedFieldname . '_1'); - if (array_key_exists($indexedFieldname, $field)) { - // It's important that we count the number of bytes - // in the field - not just the number of characters. - // UTF-8 characters can be between 1 and 4 bytes. - // - // From the strlen documentation: - // Attention with utf8: - // $foo = "bär"; - // strlen($foo) will return 4 and not 3 as expected.. - // - // So strlen does count the bytes - not the characters. - - if (is_string($field[$indexedFieldname]) && strlen($field[$indexedFieldname]) > $maxKeySize) { - $field[$indexedFieldname] = substr($field[$indexedFieldname], 0, $maxKeySize); - } + // It's important that we count the number of bytes + // in the field - not just the number of characters. + // UTF-8 characters can be between 1 and 4 bytes. + // + // From the strlen documentation: + // Attention with utf8: + // $foo = "bär"; + // strlen($foo) will return 4 and not 3 as expected.. + // + // So strlen does count the bytes - not the characters. + if (array_key_exists($indexedFieldname, $field) && (is_string($field[$indexedFieldname]) && strlen($field[$indexedFieldname]) > $maxKeySize)) { + $field[$indexedFieldname] = substr($field[$indexedFieldname], 0, $maxKeySize); } } } @@ -720,19 +675,18 @@ protected function truncateFields(Collection $collection, array &$generatedRow) } /** - * @param array $spec The table spec - * @param array $dest The table row document to save + * @param array $spec The table spec + * @param mixed[] $dest The table row document to save */ - protected function doComputedFields(array $spec, array &$dest) + protected function doComputedFields(array $spec, array &$dest): void { if (isset($spec['computed_fields'])) { foreach ($spec['computed_fields'] as $f) { if (isset($f['fieldName'], $f['value']) && is_array($f['value'])) { - if (isset($f['temporary']) && $f['temporary'] === true) { - if (!in_array($f['fieldName'], $this->temporaryFields)) { - $this->temporaryFields[] = $f['fieldName']; - } + if (isset($f['temporary']) && $f['temporary'] === true && !in_array($f['fieldName'], $this->temporaryFields)) { + $this->temporaryFields[] = $f['fieldName']; } + $computedFunctions = array_values(array_intersect(self::$computedFieldFunctions, array_keys($f['value']))); $dest[$f['fieldName']] = $this->getComputedValue($computedFunctions[0], $f['value'], $dest); } @@ -741,13 +695,13 @@ protected function doComputedFields(array $spec, array &$dest) } /** - * @param string $function A defined computed value function - * @param array $spec The computed field spec - * @param array $dest The table row document to save + * @param string $function A defined computed value function + * @param array $spec The computed field spec + * @param array $dest The table row document to save * * @return mixed The computed value */ - protected function getComputedValue($function, array $spec, array &$dest) + protected function getComputedValue(string $function, array $spec, array &$dest) { $value = null; @@ -772,6 +726,9 @@ protected function getComputedValue($function, array $spec, array &$dest) } /** + * @param array $equation + * @param array $dest + * * @return float|int|null * * @throws \InvalidArgumentException @@ -781,6 +738,7 @@ protected function computeArithmeticValue(array $equation, array &$dest) if (count($equation) < 3) { throw new \InvalidArgumentException('Equations must consist of an array with 3 values'); } + if (!in_array($equation[1], self::$arithmeticOperators)) { throw new \InvalidArgumentException('Invalid arithmetic operator'); } @@ -790,6 +748,7 @@ protected function computeArithmeticValue(array $equation, array &$dest) if (is_array($left)) { $left = $this->computeArithmeticValue($left, $dest); } + if (is_array($right)) { $right = $this->computeArithmeticValue($right, $dest); } @@ -828,8 +787,8 @@ protected function computeArithmeticValue(array $equation, array &$dest) } /** - * @param array $replaceSpec The replace value spec - * @param array $dest The table row document to save + * @param array $replaceSpec The replace value spec + * @param array $dest The table row document to save * * @return mixed */ @@ -841,9 +800,11 @@ protected function generateReplaceValue(array $replaceSpec, array &$dest) if (isset($replaceSpec['search'])) { $search = $this->rewriteVariableValue($replaceSpec['search'], $dest); } + if (isset($replaceSpec['replace'])) { $replace = $this->rewriteVariableValue($replaceSpec['replace'], $dest); } + if (isset($replaceSpec['subject'])) { $subject = $this->rewriteVariableValue($replaceSpec['subject'], $dest); } @@ -852,8 +813,8 @@ protected function generateReplaceValue(array $replaceSpec, array &$dest) } /** - * @param array $conditionalSpec The conditional spec - * @param array $dest The table row document to save + * @param array $conditionalSpec The conditional spec + * @param array $dest The table row document to save * * @return mixed The computed value */ @@ -867,9 +828,11 @@ protected function generateConditionalValue(array $conditionalSpec, array &$dest if (isset($conditionalSpec['if'][0])) { $left = $this->rewriteVariableValue($conditionalSpec['if'][0], $dest); } + if (isset($conditionalSpec['if'][1])) { $operator = $conditionalSpec['if'][1]; } + if (isset($conditionalSpec['if'][2])) { $right = $this->rewriteVariableValue($conditionalSpec['if'][2], $dest); } @@ -882,7 +845,7 @@ protected function generateConditionalValue(array $conditionalSpec, array &$dest if (is_array($conditionalSpec[$path])) { $nestedComputedFunctions = array_intersect(self::$computedFieldFunctions, array_keys($conditionalSpec[$path])); // This is 'just a regular old array' - if (empty($nestedComputedFunctions)) { + if ($nestedComputedFunctions === []) { return $this->rewriteVariableValue($conditionalSpec[$path], $dest); } @@ -897,13 +860,13 @@ protected function generateConditionalValue(array $conditionalSpec, array &$dest } /** - * @param mixed $value The value to replace, if it contains a variable - * @param array $dest The table row document to save - * @param string|null $setType Force the return to be set to specified type + * @param mixed $value The value to replace, if it contains a variable + * @param array $dest The table row document to save + * @param string|null $setType Force the return to be set to specified type * * @return mixed */ - protected function rewriteVariableValue($value, array &$dest, $setType = null) + protected function rewriteVariableValue($value, array &$dest, ?string $setType = null) { if (is_string($value)) { if (strpos($value, '$') === 0) { @@ -917,12 +880,14 @@ protected function rewriteVariableValue($value, array &$dest, $setType = null) return $this->castValueType($value, $setType); } + if (is_array($value)) { if ($this->isFunction($value)) { $function = array_keys($value); return $this->getComputedValue($function[0], $value, $dest); } + $aryValue = []; foreach ($value as $v) { $aryValue[] = $this->rewriteVariableValue($v, $dest); @@ -936,21 +901,18 @@ protected function rewriteVariableValue($value, array &$dest, $setType = null) /** * @param mixed $value - * - * @return bool */ - protected function isFunction($value) + protected function isFunction($value): bool { return is_array($value) && count(array_keys($value)) === 1 && count(array_intersect(array_keys($value), self::$computedFieldFunctions)) === 1; } /** - * @param mixed $value - * @param string|null $type + * @param mixed $value * * @return mixed */ - protected function castValueType($value, $type = null) + protected function castValueType($value, ?string $type = null) { // If value is a UTCDateTime, turn into a DateTime object in order to perform comparison if ($value instanceof UTCDateTime) { @@ -971,11 +933,7 @@ protected function castValueType($value, $type = null) case 'numeric': if ((!is_int($value)) && !is_float($value)) { - if ($value == (string) (int) $value) { - $value = (int) $value; - } else { - $value = (float) $value; - } + $value = $value == (string) (int) $value ? (int) $value : (float) $value; } break; @@ -985,22 +943,22 @@ protected function castValueType($value, $type = null) } /** - * @param mixed $left The left value of the condition - * @param string $operator The comparison operator - * @param mixed $right The right value of the condition - * - * @return bool + * @param mixed $left The left value of the condition + * @param string|null $operator The comparison operator + * @param mixed|null $right The right value of the condition * * @throws \InvalidArgumentException */ - protected function doConditional($left, $operator, $right) + protected function doConditional($left, ?string $operator, $right): bool { - if ((!empty($operator)) && !in_array($operator, self::$conditionalOperators)) { - throw new \InvalidArgumentException('Invalid conditional operator'); - } if (!$operator) { - return $left ? true : false; + return (bool) $left; + } + + if (!in_array($operator, self::$conditionalOperators)) { + throw new \InvalidArgumentException('Invalid conditional operator'); } + $result = false; switch ($operator) { @@ -1036,19 +994,16 @@ protected function doConditional($left, $operator, $right) case 'contains': case 'not contains': - if (is_array($left)) { - $bool = in_array($right, $left); - } else { - $bool = (strpos((string) $left, (string) $right) !== false); - } - $result = ($bool && $operator != 'not contains'); + $bool = is_array($left) ? in_array($right, $left) : strpos((string) $left, (string) $right) !== false; + + $result = ($bool && $operator !== 'not contains'); break; case '~=': case '!~': $match = preg_match($right, $left); - $result = ($match > 0 && $operator != '!~'); + $result = ($match > 0 && $operator !== '!~'); break; } @@ -1059,19 +1014,21 @@ protected function doConditional($left, $operator, $right) /** * Add fields to a table row. * - * @param array $source - * @param array $spec - * @param array $dest + * @param array $source + * @param array $spec + * @param array $dest */ - protected function addFields($source, $spec, &$dest) + protected function addFields(array $source, array $spec, array &$dest): void { if (isset($spec['fields'])) { foreach ($spec['fields'] as $f) { - if (isset($f['temporary']) && $f['temporary'] === true) { - if (!in_array($f['fieldName'], $this->temporaryFields)) { - $this->temporaryFields[] = $f['fieldName']; - } + /** @var string */ + $fieldName = $f['fieldName']; + + if (isset($f['temporary']) && $f['temporary'] === true && !in_array($fieldName, $this->temporaryFields)) { + $this->temporaryFields[] = $fieldName; } + if (isset($f['predicates'])) { foreach ($f['predicates'] as $p) { if (is_string($p) && isset($source[$p])) { @@ -1091,11 +1048,10 @@ protected function addFields($source, $spec, &$dest) $this->generateValues($source, $f, $v, $dest); } } + // Otherwise apply a modifier - } else { - if (isset($dest[$f['fieldName']])) { - $dest[$f['fieldName']] = $this->applyModifier($function, $dest[$f['fieldName']], $functionOptions); - } + } elseif (isset($dest[$fieldName])) { + $dest[$fieldName] = $this->applyModifier($function, $dest[$fieldName], $functionOptions); } } } @@ -1103,20 +1059,20 @@ protected function addFields($source, $spec, &$dest) } // Allow URI linking to the ID - if (isset($f['value'])) { - if ($f['value'] == '_link_' || $f['value'] == 'link') { - if ($f['value'] == '_link_') { - $this->warningLog("Table spec value '_link_' is deprecated", $f); - } - // If value exists, set as array - if (isset($dest[$f['fieldName']])) { - if (!is_array($dest[$f['fieldName']])) { - $dest[$f['fieldName']] = [$dest[$f['fieldName']]]; - } - $dest[$f['fieldName']][] = $this->labeller->qname_to_alias($source['_id']['r']); - } else { - $dest[$f['fieldName']] = $this->labeller->qname_to_alias($source['_id']['r']); + if (isset($f['value']) && ($f['value'] == '_link_' || $f['value'] == 'link')) { + if ($f['value'] == '_link_') { + $this->warningLog("Table spec value '_link_' is deprecated", $f); + } + + // If value exists, set as array + if (isset($dest[$fieldName])) { + if (!is_array($dest[$fieldName])) { + $dest[$fieldName] = [$dest[$fieldName]]; } + + $dest[$fieldName][] = $this->labeller->qname_to_alias($source['_id']['r']); + } else { + $dest[$fieldName] = $this->labeller->qname_to_alias($source['_id']['r']); } } } @@ -1126,12 +1082,11 @@ protected function addFields($source, $spec, &$dest) /** * Generate values for a given predicate. * - * @param array $source - * @param array $f - * @param string $predicate - * @param array $dest + * @param array $source + * @param array $f + * @param array $dest */ - protected function generateValues($source, $f, $predicate, &$dest) + protected function generateValues(array $source, array $f, string $predicate, array &$dest): void { $values = []; if (isset($source[$predicate][VALUE_URI]) && !empty($source[$predicate][VALUE_URI])) { @@ -1147,24 +1102,28 @@ protected function generateValues($source, $f, $predicate, &$dest) } elseif (isset($v[VALUE_URI]) && !empty($v[VALUE_URI])) { $values[] = $v[VALUE_URI]; } + // _id's shouldn't appear in value arrays, so no need for third condition here } } + /** @var string */ + $fieldName = $f['fieldName']; + // now add all the values foreach ($values as $v) { - if (!isset($dest[$f['fieldName']])) { + if (!isset($dest[$fieldName])) { // single value - $dest[$f['fieldName']] = $v; - } elseif (is_array($dest[$f['fieldName']])) { + $dest[$fieldName] = $v; + } elseif (is_array($dest[$fieldName])) { // add to existing array of values - $dest[$f['fieldName']][] = $v; + $dest[$fieldName][] = $v; } else { // convert from single value to array of values - $existingVal = $dest[$f['fieldName']]; - $dest[$f['fieldName']] = []; - $dest[$f['fieldName']][] = $existingVal; - $dest[$f['fieldName']][] = $v; + $existingVal = $dest[$fieldName]; + $dest[$fieldName] = []; + $dest[$fieldName][] = $existingVal; + $dest[$fieldName][] = $v; } } } @@ -1172,33 +1131,27 @@ protected function generateValues($source, $f, $predicate, &$dest) /** * Recursively get functions that can modify a predicate. * - * @param array $array - * - * @return array + * @param array|string $array */ - protected function getPredicateFunctions($array) + protected function getPredicateFunctions($array): array { $predicateFunctions = []; if (is_array($array)) { if (isset($array['predicates'])) { $predicateFunctions['predicates'] = $array['predicates']; - } else { - $predicateFunctions[key($array)] = $array[key($array)]; - $predicateFunctions = array_merge($predicateFunctions, $this->getPredicateFunctions($array[key($array)])); + } elseif ($array !== []) { + $function = key($array); + $predicateFunctions[$function] = $array[$function]; + if (is_array($array[$function])) { + $predicateFunctions = array_merge($predicateFunctions, $this->getPredicateFunctions($array[$function])); + } } } return $predicateFunctions; } - /** - * @param array $source - * @param array $joins - * @param array $dest - * @param string $from - * @param string $contextAlias - */ - protected function doJoins($source, $joins, &$dest, $from, $contextAlias) + protected function doJoins(array $source, array $joins, array &$dest, string $from, string $contextAlias): void { $this->expandSequence($joins, $source); foreach ($joins as $predicate => $ruleset) { @@ -1247,10 +1200,8 @@ protected function doJoins($source, $joins, &$dest, $from, $contextAlias) } } - if (count($recursiveJoins) > 0) { - foreach ($recursiveJoins as $r) { - $this->doJoins($r['data'], $r['ruleset'], $dest, $from, $contextAlias); - } + foreach ($recursiveJoins as $r) { + $this->doJoins($r['data'], $r['ruleset'], $dest, $from, $contextAlias); } } } @@ -1259,45 +1210,38 @@ protected function doJoins($source, $joins, &$dest, $from, $contextAlias) /** * Add counts to $dest by counting what is in $source according to $countSpec. * - * @param mixed $source - * @param mixed $countSpec - * @param mixed $dest + * @param int[] $dest */ - protected function doCounts($source, $countSpec, &$dest) + protected function doCounts(array $source, array $countSpec, array &$dest): void { // process count aggregate function foreach ($countSpec as $c) { $fieldName = $c['fieldName']; - if (isset($c['temporary']) && $c['temporary'] === true) { - if (!in_array($fieldName, $this->temporaryFields)) { - $this->temporaryFields[] = $fieldName; - } + if (isset($c['temporary']) && $c['temporary'] === true && !in_array($fieldName, $this->temporaryFields)) { + $this->temporaryFields[] = $fieldName; } + $applyRegex = isset($c['regex']) ?: null; $count = 0; // just count predicates at current location if (isset($source[$c['property']])) { if (isset($source[$c['property']][VALUE_URI]) || isset($source[$c['property']][VALUE_LITERAL])) { - if ($applyRegex != null) { - $count = $this->applyRegexToValue($c['regex'], $source[$c['property']]); - } else { - $count = 1; - } - } else { - if ($applyRegex != null) { - foreach ($source[$c['property']] as $value) { - if ($this->applyRegexToValue($c['regex'], $value)) { - $count++; - } + $count = $applyRegex != null ? $this->applyRegexToValue($c['regex'], $source[$c['property']]) : 1; + } elseif ($applyRegex != null) { + foreach ($source[$c['property']] as $value) { + if ($this->applyRegexToValue($c['regex'], $value)) { + $count++; } - } else { - $count = count($source[$c['property']]); } + } else { + $count = count($source[$c['property']]); } } + if (!isset($dest[$fieldName])) { $dest[$fieldName] = 0; } + $dest[$fieldName] += $count; } } @@ -1305,12 +1249,8 @@ protected function doCounts($source, $countSpec, &$dest) /** * Test if the a particular type appears in the array of types associated with a particular spec and that the changeset * includes rdf:type (or is empty, meaning addition or deletion vs. update). - * - * @param string $rdfType - * - * @return bool */ - protected function checkIfTypeShouldTriggerOperation($rdfType, array $validTypes, array $subjectPredicates) + protected function checkIfTypeShouldTriggerOperation(string $rdfType, array $validTypes, array $subjectPredicates): bool { // We don't know if this is an alias or a fqURI, nor what is in the valid types, necessarily $types = [$rdfType]; @@ -1326,16 +1266,18 @@ protected function checkIfTypeShouldTriggerOperation($rdfType, array $validTypes } $intersectingTypes = array_unique(array_intersect($types, $validTypes)); - if (!empty($intersectingTypes)) { + if ($intersectingTypes !== []) { // Table rows only need to be invalidated if their rdf:type property has changed // This means we're either adding or deleting a graph - if (empty($subjectPredicates)) { + if ($subjectPredicates === []) { return true; } + // Check for alias in changed predicates if (in_array('rdf:type', $subjectPredicates)) { return true; } + // Check for fully qualified URI in changed predicates if (in_array(RDF_TYPE, $subjectPredicates)) { return true; @@ -1349,10 +1291,8 @@ protected function checkIfTypeShouldTriggerOperation($rdfType, array $validTypes * For mocking. * * @param string $tableSpecId Table spec ID - * - * @return Collection */ - protected function getCollectionForTableSpec($tableSpecId) + protected function getCollectionForTableSpec(string $tableSpecId): Collection { return $this->getConfigInstance()->getCollectionForTable($this->storeName, $tableSpecId); } @@ -1364,68 +1304,66 @@ protected function getCollectionForTableSpec($tableSpecId) * join - pass in "glue":" " to specify what to glue multiple values together with * date - no options. * - * @param string $modifier - * @param string $value - * @param array $options + * @param string|string[]|\Stringable|\Stringable[] $value + * @param array $options * * @return mixed * * @throws \Exception */ - private function applyModifier($modifier, $value, $options = []) + private function applyModifier(string $modifier, $value, array $options = []) { - try { - switch ($modifier) { - case 'predicates': - // Used to generate a list of values - does nothing here - break; - - case 'lowercase': - if (is_array($value)) { - $value = array_map('strtolower', $value); - } else { - $value = strtolower($value); - } + switch ($modifier) { + case 'predicates': + // Used to generate a list of values - does nothing here + break; - break; + case 'lowercase': + $value = is_array($value) ? array_map([$this, 'strtolower'], $value) : $this->strtolower($value); - case 'join': - if (is_array($value)) { - $value = implode($options['glue'], $value); - } + break; - break; + case 'join': + if (is_array($value)) { + $value = implode($options['glue'], $value); + } - case 'date': - if (is_string($value)) { - $value = DateUtil::getMongoDate(strtotime($value) * 1000); - } + break; - break; + case 'date': + if (is_string($value)) { + $value = DateUtil::getMongoDate(strtotime($value) * 1000); + } - default: - throw new \Exception('Could not apply modifier:' . $modifier); + break; - break; - } - } catch (\Exception $e) { - throw $e; + default: + throw new \Exception('Could not apply modifier:' . $modifier); } return $value; } + /** + * Lowercase a value, casting to string first. + * + * @param string|\Stringable $value + */ + private function strtolower($value): string + { + return strtolower((string) $value); + } + /** * Apply a regex to the RDF property value defined in $value. * - * @param mixed $regex - * @param mixed $value + * @param array $value * - * @return int + * @return false|int * * @throws \Tripod\Exceptions\Exception */ - private function applyRegexToValue($regex, $value) + private function applyRegexToValue(string $regex, array $value) { if (isset($value[VALUE_URI]) || isset($value[VALUE_LITERAL])) { $v = $value[VALUE_URI] ?: $value[VALUE_LITERAL]; diff --git a/src/mongo/delegates/TransactionLog.php b/src/mongo/delegates/TransactionLog.php index 7c34bf8b..e66d8753 100644 --- a/src/mongo/delegates/TransactionLog.php +++ b/src/mongo/delegates/TransactionLog.php @@ -1,17 +1,23 @@ config = $config->getTransactionLogConfig(); + $this->transaction_db = $config->getTransactionLogDatabase(); $this->transaction_collection = $this->transaction_db->selectCollection($this->config['collection']); } /** - * @param string $transaction_id - the id you wish to assign to the new transaction - * @param array $changes - an array serialization of the changeset to be applied - * @param array $originalCBDs - an array of the serialized CBDs - * @param string $storeName - the name of the database the changes are being applied to - * @param string $podName - the name of the collection, in the database, the changes are being applied to + * @param string $transaction_id - the id you wish to assign to the new transaction + * @param array $changes - an array serialization of the changeset to be applied + * @param array|null $originalCBDs - an array of the serialized CBDs + * @param string $storeName - the name of the database the changes are being applied to + * @param string $podName - the name of the collection, in the database, the changes are being applied to * * @throws \Tripod\Exceptions\Exception */ - public function createNewTransaction($transaction_id, $changes, $originalCBDs, $storeName, $podName) + public function createNewTransaction(string $transaction_id, array $changes, ?array $originalCBDs, string $storeName, string $podName): void { $transaction = [ '_id' => $transaction_id, @@ -52,7 +59,7 @@ public function createNewTransaction($transaction_id, $changes, $originalCBDs, $ throw new \Exception('Error creating new transaction'); } } catch (\Exception $e) { - throw new \Tripod\Exceptions\Exception('Error creating new transaction: ' . $e->getMessage()); + throw new \Tripod\Exceptions\Exception('Error creating new transaction: ' . $e->getMessage(), $e->getCode(), $e); } } @@ -61,9 +68,9 @@ public function createNewTransaction($transaction_id, $changes, $originalCBDs, $ * If you passed in an Exception, the exception is logged in the transaction log. * * @param string $transaction_id the id of the transaction you wish to cancel - * @param \Exception $error pass in the exception you wish to log + * @param \Throwable $error pass in the exception you wish to log */ - public function cancelTransaction($transaction_id, ?\Exception $error = null) + public function cancelTransaction(string $transaction_id, ?\Throwable $error = null): void { $params = ['status' => 'cancelling']; if ($error != null) { @@ -82,9 +89,9 @@ public function cancelTransaction($transaction_id, ?\Exception $error = null) * If you passed in an Exception, the exception is logged in the transaction log. * * @param string $transaction_id the id of the transaction you wish to set as failed - * @param \Exception $error exception you wish to log + * @param \Throwable $error exception you wish to log */ - public function failTransaction($transaction_id, ?\Exception $error = null) + public function failTransaction(string $transaction_id, ?\Throwable $error = null): void { $params = ['status' => 'failed', 'failedTime' => DateUtil::getMongoDate()]; if ($error != null) { @@ -104,7 +111,7 @@ public function failTransaction($transaction_id, ?\Exception $error = null) * @param string $transaction_id - the id of the transaction you want to mark as completed * @param array $newCBDs array of CBD's that represent the after state for each modified entity */ - public function completeTransaction($transaction_id, array $newCBDs) + public function completeTransaction(string $transaction_id, array $newCBDs): void { $this->updateTransaction( ['_id' => $transaction_id], @@ -118,9 +125,9 @@ public function completeTransaction($transaction_id, array $newCBDs) * * @param string $transaction_id - the id of the transaction you wish to retrieve from the transaction log * - * @return array representing the transaction document + * @return array|null representing the transaction document */ - public function getTransaction($transaction_id) + public function getTransaction(string $transaction_id): ?array { return $this->transaction_collection->findOne(['_id' => $transaction_id]); } @@ -128,22 +135,20 @@ public function getTransaction($transaction_id) /** * Purges all transactions from the transaction log. */ - public function purgeAllTransactions() + public function purgeAllTransactions(): void { $this->transaction_collection->drop(); } /** - * @param string $storeName - * @param string $podName - * @param string|null $fromDate only transactions after this specified date. This must be a datetime string i.e. '2010-01-15 00:00:00' - * @param string|null $toDate only transactions before this specified date. This must be a datetime string i.e. '2010-01-15 00:00:00' + * @param string|null $fromDate only transactions after this specified date. This must be a datetime string i.e. '2010-01-15 00:00:00' + * @param string|null $toDate only transactions before this specified date. This must be a datetime string i.e. '2010-01-15 00:00:00' * - * @return Cursor + * @return CursorInterface&\Iterator * * @throws \InvalidArgumentException */ - public function getCompletedTransactions($storeName = null, $podName = null, $fromDate = null, $toDate = null) + public function getCompletedTransactions(?string $storeName = null, ?string $podName = null, ?string $fromDate = null, ?string $toDate = null) { $query = []; $query['status'] = 'completed'; @@ -170,20 +175,20 @@ public function getCompletedTransactions($storeName = null, $podName = null, $fr /** * @return int Total number of transactions in the transaction log */ - public function getTotalTransactionCount() + public function getTotalTransactionCount(): int { return $this->transaction_collection->count([]); } /** - * @param string $storeName database name to filter on (optional) - * @param string $podName collectionName to filter on (optional) + * @param string|null $storeName database name to filter on (optional) + * @param string|null $podName collectionName to filter on (optional) * * @return int Total number of completed transactions in the transaction log * * @codeCoverageIgnore */ - public function getCompletedTransactionCount($storeName = null, $podName = null) + public function getCompletedTransactionCount(?string $storeName = null, ?string $podName = null): int { if (!empty($storeName) && !empty($podName)) { return $this->transaction_collection->count(['status' => 'completed', 'dbName' => $storeName, 'collectionName' => $podName]); @@ -197,13 +202,9 @@ public function getCompletedTransactionCount($storeName = null, $podName = null) /** * Proxy method to help with test mocking. * - * @param array $transaction - * - * @return InsertOneResult - * * @codeCoverageIgnore */ - protected function insertTransaction($transaction) + protected function insertTransaction(array $transaction): InsertOneResult { return $this->transaction_collection->insertOne($transaction, ['w' => 1]); } @@ -211,15 +212,9 @@ protected function insertTransaction($transaction) /** * Proxy method to help with test mocking. * - * @param array $query - * @param array $update - * @param array $options - * - * @return UpdateOneResult - * * @codeCoverageIgnore */ - protected function updateTransaction($query, $update, $options) + protected function updateTransaction(array $query, array $update, array $options): UpdateResult { return $this->transaction_collection->updateOne($query, $update, $options); } diff --git a/src/mongo/delegates/Updates.php b/src/mongo/delegates/Updates.php index 016c07ac..79ba7bfb 100644 --- a/src/mongo/delegates/Updates.php +++ b/src/mongo/delegates/Updates.php @@ -1,5 +1,7 @@ tripod = $tripod; $this->storeName = $tripod->getStoreName(); @@ -97,7 +75,8 @@ public function __construct(Driver $tripod, $opts = []) OP_ASYNC => [OP_VIEWS => false, OP_TABLES => true, OP_SEARCH => true], 'stat' => null, 'readPreference' => ReadPreference::RP_PRIMARY_PREFERRED, - 'retriesToGetLock' => 20], $opts); + 'retriesToGetLock' => 20, + ], $opts); $this->readPreference = $opts['readPreference']; $this->config = $this->getConfigInstance(); @@ -112,6 +91,7 @@ public function __construct(Driver $tripod, $opts = []) if (!array_key_exists(OP_VIEWS, $async)) { $async[OP_VIEWS] = false; } + if (!array_key_exists(OP_TABLES, $async)) { $async[OP_TABLES] = true; } @@ -141,19 +121,14 @@ public function __construct(Driver $tripod, $opts = []) /** * Create and apply a changeset which is the delta between $oldGraph and $newGraph. * - * @param string|null $context - * @param string|null $description - * - * @return bool - * * @throws \Exception */ public function saveChanges( ExtendedGraph $oldGraph, ExtendedGraph $newGraph, - $context = null, - $description = null - ) { + ?string $context = null, + ?string $description = null + ): bool { $this->applyHooks($this::HOOK_FN_PRE, $this->saveChangesHooks, [ 'pod' => $this->getPodName(), 'oldGraph' => $oldGraph, @@ -187,6 +162,7 @@ public function saveChanges( throw new Exception('Result of storeChanges malformed, should have transaction_id and subjectsAndPredicatesOfChange array keys'); } + extract($result); // will unpack into $subjectsAndPredicatesOfChange // Process any syncronous operations @@ -224,16 +200,10 @@ public function saveChanges( } // ////// LOCKS \\\\\\\\ - /** * Get locked documents for a date range or all documents if no date range is given. - * - * @param string $fromDateTime - * @param string $tillDateTime - * - * @return array */ - public function getLockedDocuments($fromDateTime = null, $tillDateTime = null) + public function getLockedDocuments(?string $fromDateTime = null, ?string $tillDateTime = null): array { $query = []; if (!empty($fromDateTime) || !empty($tillDateTime)) { @@ -242,10 +212,12 @@ public function getLockedDocuments($fromDateTime = null, $tillDateTime = null) if (!empty($fromDateTime)) { $query[_LOCKED_FOR_TRANS_TS][MONGO_OPERATION_GTE] = DateUtil::getMongoDate(strtotime($fromDateTime) * 1000); } + if (!empty($tillDateTime)) { $query[_LOCKED_FOR_TRANS_TS][MONGO_OPERATION_LTE] = DateUtil::getMongoDate(strtotime($tillDateTime) * 1000); } } + $docs = $this->getLocksCollection()->find($query, ['sort' => [_LOCKED_FOR_TRANS => 1]]); if ($this->getLocksCollection()->count($query) == 0) { @@ -263,14 +235,9 @@ public function getLockedDocuments($fromDateTime = null, $tillDateTime = null) /** * Remove locks that are there forever, creates a audit entry to keep track who and why removed these locks. * - * @param string $transaction_id - * @param string $reason - * - * @return bool - * - * @throws \Exception, if something goes wrong when unlocking documents, or creating audit entries + * @throws \Exception If something goes wrong when unlocking documents, or creating audit entries */ - public function removeInertLocks($transaction_id, $reason) + public function removeInertLocks(string $transaction_id, string $reason): bool { $query = [_LOCKED_FOR_TRANS => $transaction_id]; $docs = $this->getLocksCollection()->find($query); @@ -352,17 +319,14 @@ public function removeInertLocks($transaction_id, $reason) } // /////// REPLAY TRANSACTION LOG /////// - /** * replays all transactions from the transaction log, use the function params to control the from and to date if you * only want to replay transactions created during specific window. * * @param string|null $fromDate only transactions after this specified date. This must be a datetime string i.e. '2010-01-15 00:00:00' * @param string|null $toDate only transactions before this specified date. This must be a datetime string i.e. '2010-01-15 00:00:00' - * - * @return bool */ - public function replayTransactionLog($fromDate = null, $toDate = null) + public function replayTransactionLog(?string $fromDate = null, ?string $toDate = null): bool { $cursor = $this->getTransactionLog()->getCompletedTransactions($this->storeName, $this->podName, $fromDate, $toDate); foreach ($cursor as $result) { @@ -373,11 +337,7 @@ public function replayTransactionLog($fromDate = null, $toDate = null) } // getters and setters for the delegates - - /** - * @return TransactionLog - */ - public function getTransactionLog() + public function getTransactionLog(): TransactionLog { if ($this->transactionLog == null) { $this->transactionLog = new TransactionLog(); @@ -386,7 +346,7 @@ public function getTransactionLog() return $this->transactionLog; } - public function setTransactionLog(TransactionLog $transactionLog) + public function setTransactionLog(TransactionLog $transactionLog): void { $this->transactionLog = $transactionLog; } @@ -394,16 +354,43 @@ public function setTransactionLog(TransactionLog $transactionLog) /** * Register save changes event hooks. */ - public function registerSaveChangesEventHook(IEventHook $hook) + public function registerSaveChangesEventHook(IEventHook $hook): void { $this->saveChangesHooks[] = $hook; } + /** + * @param IEventHook[] $hooks + * + * @throws Exception If an invalid hook function is requested + */ + protected function applyHooks(string $fn, array $hooks, array $args = []): void + { + switch ($fn) { + case $this::HOOK_FN_PRE: + case $this::HOOK_FN_SUCCESS: + case $this::HOOK_FN_FAILURE: + break; + + default: + throw new Exception(sprintf('Invalid hook function %s requested', $fn)); + } + + foreach ($hooks as $hook) { + try { + call_user_func([$hook, $fn], $args); + } catch (\Exception $e) { + // don't let rabid hooks stop tripod + static::getLogger()->error('Hook ' . get_class($hook) . sprintf(' threw exception %s, continuing', $e->getMessage())); + } + } + } + /** * Change the read preferences to RP_PRIMARY * Used for a write operation. */ - protected function setReadPreferenceToPrimary() + protected function setReadPreferenceToPrimary(): void { // Set db preference /** @var ReadPreference $dbReadPref */ @@ -432,12 +419,11 @@ protected function setReadPreferenceToPrimary() /** * Reset the original read preference after changing with setReadPreferenceToPrimary. */ - protected function resetOriginalReadPreference() + protected function resetOriginalReadPreference(): void { - /** @var ReadPreference $dbReadPref */ - $dbReadPref = $this->db->__debugInfo()['readPreference']; + $dbReadPref = $this->db->getReadPreference(); if ($this->originalDbReadPreference !== $dbReadPref->getMode()) { - $pref = $this->originalDbReadPreference ?? $this->readPreference; + $pref = $this->originalDbReadPreference ?: $this->readPreference; $dbTagsets = $dbReadPref->getTagsets(); $this->db = $this->db->withOptions([ @@ -446,10 +432,9 @@ protected function resetOriginalReadPreference() } // Reset collection object - /** @var ReadPreference $collReadPref */ - $collReadPref = $this->getCollection()->__debugInfo()['readPreference']; + $collReadPref = $this->getCollection()->getReadPreference(); if ($this->originalCollectionReadPreference !== $collReadPref->getMode()) { - $pref = $this->originalCollectionReadPreference ?? $this->readPreference; + $pref = $this->originalCollectionReadPreference ?: $this->readPreference; $collTagsets = $collReadPref->getTagsets(); $this->collection = $this->collection->withOptions([ 'readPreference' => new ReadPreference($pref, $collTagsets), @@ -462,7 +447,7 @@ protected function resetOriginalReadPreference() * * @throws CardinalityException */ - protected function validateGraphCardinality(ExtendedGraph $graph) + protected function validateGraphCardinality(ExtendedGraph $graph): void { $config = $this->getConfigInstance(); $cardinality = $config->getCardinality($this->getStoreName(), $this->getPodName()); @@ -476,7 +461,7 @@ protected function validateGraphCardinality(ExtendedGraph $graph) foreach ($cardinality as $qname => $cardinalityValue) { [$namespace, $predicateName] = explode(':', $qname); if (!array_key_exists($namespace, $namespaces)) { - throw new CardinalityException("Namespace '{$namespace}' not defined for qname: {$qname}"); + throw new CardinalityException(sprintf("Namespace '%s' not defined for qname: %s", $namespace, $qname)); } // NB: The only constraint we currently support is a value of 1 to enforce one triple per subject/predicate. @@ -490,7 +475,7 @@ protected function validateGraphCardinality(ExtendedGraph $graph) $v[] = $predicateValue['value']; } - throw new CardinalityException("Cardinality failed on {$subjectUri} for '{$qname}' - should only have 1 value and has: " . implode(', ', $v)); + throw new CardinalityException(sprintf("Cardinality failed on %s for '%s' - should only have 1 value and has: ", $subjectUri, $qname) . implode(', ', $v)); } } } @@ -498,14 +483,13 @@ protected function validateGraphCardinality(ExtendedGraph $graph) } /** - * @param ChangeSet $cs Change-set to apply - * @param string $contextAlias + * @param ChangeSet $cs Change-set to apply * * @return array An array of subjects and predicates that have been changed * * @throws Exception */ - protected function storeChanges(ChangeSet $cs, $contextAlias) + protected function storeChanges(ChangeSet $cs, string $contextAlias): array { $t = new Timer(); $t->start(); @@ -516,6 +500,7 @@ protected function storeChanges(ChangeSet $cs, $contextAlias) // store the details of the transaction in the transaction log $mongoGraph = new MongoGraph(); $mongoGraph->_index = $cs->_index; + $csDoc = $mongoGraph->to_tripod_view_array('changes', $contextAlias); // todo - this changed to tripod view array, why is "changes" the docId? $originalCBDs = []; @@ -552,7 +537,7 @@ protected function storeChanges(ChangeSet $cs, $contextAlias) $t->stop(); $this->timingLog(MONGO_WRITE, ['duration' => $t->result(), 'subjectsOfChange' => implode(', ', $subjectsOfChange)]); - $this->getStat()->timer(MONGO_WRITE . ".{$this->getPodName()}", $t->result()); + $this->getStat()->timer(MONGO_WRITE . ('.' . $this->getPodName()), $t->result()); return $changes; } catch (\Exception $e) { @@ -568,19 +553,17 @@ protected function storeChanges(ChangeSet $cs, $contextAlias) ); $this->rollbackTransaction($transaction_id, $originalCBDs, $e); - throw new Exception('Error storing changes: ' . $e->getMessage() . ' >>>' . $e->getTraceAsString()); + throw new Exception('Error storing changes: ' . $e->getMessage() . ' >>>' . $e->getTraceAsString(), $e->getCode(), $e); } } /** - * @param string $transaction_id id of the transaction - * @param array $originalCBDs containing the original CBDS - * - * @return bool + * @param string $transaction_id id of the transaction + * @param array|null $originalCBDs containing the original CBDS * - * @throws \Exception + * @throws \Throwable */ - protected function rollbackTransaction($transaction_id, $originalCBDs, \Exception $exception) + protected function rollbackTransaction(string $transaction_id, ?array $originalCBDs, \Throwable $exception): bool { // set transaction to cancelling $this->getTransactionLog()->cancelTransaction($transaction_id, $exception); @@ -601,7 +584,7 @@ protected function rollbackTransaction($transaction_id, $originalCBDs, \Exceptio ] ); - throw new \Exception("Failed to restore Original CBDS for transaction: {$transaction_id} stopped at " . $g[_ID_KEY]); + throw new \Exception(sprintf('Failed to restore Original CBDS for transaction: %s stopped at ', $transaction_id) . $g[_ID_KEY]); } } } else { @@ -614,6 +597,7 @@ protected function rollbackTransaction($transaction_id, $originalCBDs, \Exceptio ] ); } + $this->unlockAllDocuments($transaction_id); // set transaction to failed @@ -624,20 +608,16 @@ protected function rollbackTransaction($transaction_id, $originalCBDs, \Exceptio /** * Returns a unique transaction ID. - * - * @return string */ - protected function generateTransactionId() + protected function generateTransactionId(): string { return 'transaction_' . $this->getUniqId(); } /** * Returns a unique id: for mocking. - * - * @return string */ - protected function getUniqId() + protected function getUniqId(): string { return uniqid('', true); } @@ -645,15 +625,11 @@ protected function getUniqId() /** * Adds/updates/deletes the graph in the database. * - * @param array $originalCBDs - * @param string $contextAlias - * @param string $transaction_id - * - * @return array + * @return array * * @throws \Exception */ - protected function applyChangeSet(ChangeSet $cs, $originalCBDs, $contextAlias, $transaction_id) + protected function applyChangeSet(ChangeSet $cs, array $originalCBDs, string $contextAlias, string $transaction_id): array { $subjectsAndPredicatesOfChange = []; if (in_array($this->getCollection()->getCollectionName(), $this->getConfigInstance()->getPods($this->getStoreName()))) { @@ -714,21 +690,23 @@ protected function applyChangeSet(ChangeSet $cs, $originalCBDs, $contextAlias, $ if (isset($additionsRemovals['removals'])) { $elemsToRemove = []; foreach ($additionsRemovals['removals'] as $removal) { - $valueIndex = array_search($removal, $valueObject); + $valueIndex = array_search($removal, $valueObject, true); if ($valueIndex === false) { $values = array_values($removal); $v = array_pop($values); - $this->errorLog("Removal value {$subjectOfChange} {$predicate} {$v} does not appear in target document to be updated", ['doc' => $doc]); + $this->errorLog(sprintf('Removal value %s %s %s does not appear in target document to be updated', $subjectOfChange, $predicate, $v), ['doc' => $doc]); - throw new \Exception("Removal value {$subjectOfChange} {$predicate} {$v} does not appear in target document to be updated"); + throw new \Exception(sprintf('Removal value %s %s %s does not appear in target document to be updated', $subjectOfChange, $predicate, $v)); } $elemsToRemove[] = $valueIndex; } - if (count($elemsToRemove) > 0) { + + if ($elemsToRemove !== []) { foreach ($elemsToRemove as $elem) { unset($valueObject[$elem]); } + $valueObject = array_values($valueObject); // renumbers array after unsets } } @@ -736,16 +714,14 @@ protected function applyChangeSet(ChangeSet $cs, $originalCBDs, $contextAlias, $ if (count($valueObject) > 0) { // unique value object $valueObject = array_map('unserialize', array_unique(array_map('serialize', $valueObject))); - - if (count($valueObject) == 1) { + if (count($valueObject) === 1) { $valueObject = $valueObject[0]; // un-array if only one value } + $this->addOperatorToChange($mongoUpdateOperations, MONGO_OPERATION_SET, [$nsPredicate => $valueObject]); - } else { + } elseif ($predicateExists) { // remove all existing values, if existed in the first place - if ($predicateExists) { - $this->addOperatorToChange($mongoUpdateOperations, MONGO_OPERATION_UNSET, [$nsPredicate => 1]); - } + $this->addOperatorToChange($mongoUpdateOperations, MONGO_OPERATION_UNSET, [$nsPredicate => 1]); } } @@ -774,7 +750,9 @@ protected function applyChangeSet(ChangeSet $cs, $originalCBDs, $contextAlias, $ 'upsert' => $update['upsert'], 'returnDocument' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER, ]); - array_push($newCBDs, $newDoc); + if ($newDoc) { + $newCBDs[] = $newDoc; + } } catch (\Exception $e) { $this->errorLog( MONGO_WRITE, @@ -784,7 +762,7 @@ protected function applyChangeSet(ChangeSet $cs, $originalCBDs, $contextAlias, $ ] ); - throw new \Exception($e); + throw $e; } } @@ -801,9 +779,9 @@ protected function applyChangeSet(ChangeSet $cs, $originalCBDs, $contextAlias, $ /** * Normalize our subjects and predicates of change to use aliases rather than fq uris. * - * @return array + * @param array> $subjectsAndPredicatesOfChange */ - protected function subjectsAndPredicatesOfChangeUrisToAliases(array $subjectsAndPredicatesOfChange) + protected function subjectsAndPredicatesOfChangeUrisToAliases(array $subjectsAndPredicatesOfChange): array { $aliases = []; foreach ($subjectsAndPredicatesOfChange as $subject => $predicates) { @@ -820,18 +798,13 @@ protected function subjectsAndPredicatesOfChangeUrisToAliases(array $subjectsAnd /** * Given a set of CBD's return the CBD that matches the Subject of Change. * - * @param string $subjectOfChange - * @param string $contextAlias - * * @return array|null the document from the collection of $cbds that matches the subject of change */ - protected function getDocumentForUpdate($subjectOfChange, $contextAlias, array $cbds) + protected function getDocumentForUpdate(string $subjectOfChange, string $contextAlias, array $cbds): ?array { foreach ($cbds as $c) { if ($c[_ID_KEY] == [_ID_RESOURCE => $this->labeller->uri_to_alias($subjectOfChange), _ID_CONTEXT => $contextAlias]) { return $c; - - break; } } @@ -840,10 +813,8 @@ protected function getDocumentForUpdate($subjectOfChange, $contextAlias, array $ /** * Processes each subject synchronously. - * - * @param string $contextAlias */ - protected function processSyncOperations(array $subjectsAndPredicatesOfChange, $contextAlias) + protected function processSyncOperations(array $subjectsAndPredicatesOfChange, string $contextAlias): void { foreach ($this->getSyncOperations() as $op) { /** @var IComposite $composite */ @@ -877,13 +848,11 @@ protected function processSyncOperations(array $subjectsAndPredicatesOfChange, $ /** * Adds the operations to the queue to be performed asynchronously. - * - * @param string $contextAlias */ - protected function queueASyncOperations(array $subjectsAndPredicatesOfChange, $contextAlias) + protected function queueASyncOperations(array $subjectsAndPredicatesOfChange, string $contextAlias): void { $operations = $this->getAsyncOperations(); - if (!empty($operations)) { + if ($operations !== []) { $data = [ 'changes' => $subjectsAndPredicatesOfChange, 'operations' => $operations, @@ -893,7 +862,7 @@ protected function queueASyncOperations(array $subjectsAndPredicatesOfChange, $c 'statsConfig' => $this->getStatsConfig(), ]; - if (isset($this->queueName)) { + if ($this->queueName !== null) { $data[OP_QUEUE] = $this->queueName; $queueName = $this->queueName; } else { @@ -907,12 +876,10 @@ protected function queueASyncOperations(array $subjectsAndPredicatesOfChange, $c /** * For mocking. - * - * @return DiscoverImpactedSubjects */ - protected function getDiscoverImpactedSubjects() + protected function getDiscoverImpactedSubjects(): DiscoverImpactedSubjects { - if (!isset($this->discoverImpactedSubjects)) { + if ($this->discoverImpactedSubjects === null) { $this->discoverImpactedSubjects = new DiscoverImpactedSubjects(); } @@ -924,13 +891,12 @@ protected function getDiscoverImpactedSubjects() * * @param array $subjectsOfChange array of the subjects that are part of this transaction * @param string $transaction_id id for this transaction - * @param string $contextAlias * * @return array|null returns an array of CBDs, each CBD is the version at the time at which the lock was attained * * @throws \Exception */ - protected function lockAllDocuments($subjectsOfChange, $transaction_id, $contextAlias) + protected function lockAllDocuments(array $subjectsOfChange, string $transaction_id, string $contextAlias): ?array { for ($retry = 1; $retry <= $this->retriesToGetLock; $retry++) { $originalCBDs = []; @@ -964,15 +930,16 @@ protected function lockAllDocuments($subjectsOfChange, $transaction_id, $context } } - if (count($subjectsOfChange) == count($lockedSubjects)) { + if (count($subjectsOfChange) === count($lockedSubjects)) { // if all subjects of change locked, we are good. return $originalCBDs; } // If any subject was locked, unlock it - if (count($lockedSubjects)) { + if ($lockedSubjects !== []) { $this->unlockAllDocuments($transaction_id); } + $this->debugLog( MONGO_LOCK, [ @@ -1005,11 +972,9 @@ protected function lockAllDocuments($subjectsOfChange, $transaction_id, $context * * @param string $transaction_id id for this transaction * - * @return bool - * * @throws \Exception is thrown if for any reason the update to mongo fails */ - protected function unlockAllDocuments($transaction_id) + protected function unlockAllDocuments(string $transaction_id): bool { $result = $this->getLocksCollection()->deleteMany([_LOCKED_FOR_TRANS => $transaction_id], ['w' => 1]); @@ -1031,20 +996,19 @@ protected function unlockAllDocuments($transaction_id) /** * Lock and return a single document for editing. * - * @param string $s subject URI of resource to lock - * @param string $transaction_id - * @param string $contextAlias + * @param string $s subject URI of resource to lock * - * @return array + * @return array|false|null */ - protected function lockSingleDocument($s, $transaction_id, $contextAlias) + protected function lockSingleDocument(string $s, string $transaction_id, string $contextAlias) { $countEntriesInLocksCollection = $this->getLocksCollection() ->count( [ _ID_KEY => [ _ID_RESOURCE => $this->labeller->uri_to_alias($s), - _ID_CONTEXT => $contextAlias], + _ID_CONTEXT => $contextAlias, + ], ] ); @@ -1093,6 +1057,7 @@ protected function lockSingleDocument($s, $transaction_id, $contextAlias) if (!$result->isAcknowledged()) { throw new \Exception('Failed to create new document: write not acknowledged'); } + $document = $this->getCollection()->findOne([_ID_KEY => [_ID_RESOURCE => $this->labeller->uri_to_alias($s), _ID_CONTEXT => $contextAlias]]); } catch (\Exception $e) { $this->errorLog( @@ -1113,35 +1078,27 @@ protected function lockSingleDocument($s, $transaction_id, $contextAlias) } // / Collection methods - - /** - * @return Collection - */ - protected function getAuditManualRollbacksCollection() + protected function getAuditManualRollbacksCollection(): Collection { return $this->config->getCollectionForManualRollbackAudit($this->storeName); } - /** - * @return ObjectId - */ - protected function generateIdForNewMongoDocument() + protected function generateIdForNewMongoDocument(): ObjectId { return new ObjectId(); } - /** - * @return UTCDateTime - */ - protected function getMongoDate() + protected function getMongoDate(): UTCDateTime { return DateUtil::getMongoDate(); } /** * Saves a transaction. + * + * @param array $transaction */ - protected function applyTransaction(array $transaction) + protected function applyTransaction(array $transaction): void { $changes = $transaction['changes']; $newCBDs = $transaction['newCBDs']; @@ -1149,7 +1106,7 @@ protected function applyTransaction(array $transaction) $subjectsOfChange = []; foreach ($changes as $c) { if ($c['rdf:type'][VALUE_URI] == 'cs:ChangeSet') { - array_push($subjectsOfChange, $c['cs:subjectOfChange']['u']); + $subjectsOfChange[] = $c['cs:subjectOfChange']['u']; } } @@ -1167,11 +1124,9 @@ protected function applyTransaction(array $transaction) /** * Creates a new Driver instance. * - * @param array $data - * - * @return Driver + * @param array $data */ - protected function getTripod($data) + protected function getTripod(array $data): Driver { return new Driver( $data['collection'], @@ -1183,49 +1138,27 @@ protected function getTripod($data) /** * This proxy method allows us to mock updates against $this->collection. * - * @param mixed $query - * @param mixed $update - * @param mixed $options - * - * @return bool + * @param array|object $query + * @param array|object $update + * @param array $options */ - protected function updateCollection($query, $update, $options) + protected function updateCollection($query, $update, array $options): UpdateResult { return $this->getCollection()->replaceOne($query, $update, $options); } - /** - * Returns the context alias curie for the supplied context or default context. - * - * @param string|null $context - * - * @return string - */ - protected function getContextAlias($context = null) - { - $contextAlias = $this->labeller->uri_to_alias((empty($context)) ? $this->defaultContext : $context); - - return (empty($contextAlias)) ? $this->getConfigInstance()->getDefaultContextAlias() : $contextAlias; - } - - /** - * @return Database - */ - protected function getLocksDatabase() + protected function getLocksDatabase(): Database { - if (!isset($this->locksDb)) { + if ($this->locksDb === null) { $this->locksDb = $this->config->getDatabase($this->storeName); } return $this->locksDb; } - /** - * @return Collection - */ - protected function getLocksCollection() + protected function getLocksCollection(): Collection { - if (!isset($this->locksCollection)) { + if ($this->locksCollection === null) { $this->locksCollection = $this->getLocksDatabase()->selectCollection(LOCKS_COLLECTION); } @@ -1234,12 +1167,8 @@ protected function getLocksCollection() /** * Helper function to group the changes for $changeUri by namespaced predicate, then by additions and removals. - * - * @param mixed $changeUri - * - * @return array */ - private function getAdditionsRemovalsGroupedByNsPredicate(ChangeSet $cs, $changeUri) + private function getAdditionsRemovalsGroupedByNsPredicate(ChangeSet $cs, string $changeUri): array { $additionsGroupedByNsPredicate = $this->getChangesGroupedByNsPredicate($cs, $changeUri, $this->labeller->qname_to_uri('cs:addition')); $removalsGroupedByNsPredicate = $this->getChangesGroupedByNsPredicate($cs, $changeUri, $this->labeller->qname_to_uri('cs:removal')); @@ -1249,12 +1178,15 @@ private function getAdditionsRemovalsGroupedByNsPredicate(ChangeSet $cs, $change if (!isset($mergedResult[$predicate])) { $mergedResult[$predicate] = []; } + $mergedResult[$predicate]['additions'] = $values; } + foreach ($removalsGroupedByNsPredicate as $predicate => $values) { if (!isset($mergedResult[$predicate])) { $mergedResult[$predicate] = []; } + $mergedResult[$predicate]['removals'] = $values; } @@ -1264,14 +1196,11 @@ private function getAdditionsRemovalsGroupedByNsPredicate(ChangeSet $cs, $change /** * Helper method to group changes for $changeUri of a given type by namespaced predicate. * - * @param mixed $changeUri - * @param mixed $changePredicate - * - * @return array + * @param array|string $changePredicate * * @throws Exception */ - private function getChangesGroupedByNsPredicate(ChangeSet $cs, $changeUri, $changePredicate) + private function getChangesGroupedByNsPredicate(ChangeSet $cs, string $changeUri, $changePredicate): array { $changes = $cs->get_subject_property_values($changeUri, $changePredicate); @@ -1285,7 +1214,7 @@ private function getChangesGroupedByNsPredicate(ChangeSet $cs, $changeUri, $chan } $object = $cs->get_subject_property_values($c['value'], $this->labeller->qname_to_uri('rdf:object')); - if (count($object) != 1) { + if (count($object) !== 1) { $this->getLogger()->error('Expecting object array with exactly 1 element', $object); throw new Exception('Object of removal malformed'); @@ -1303,27 +1232,32 @@ private function getChangesGroupedByNsPredicate(ChangeSet $cs, $changeUri, $chan /** * Helper method to add operator to a set of existing changes ready to be sent to Mongo. * - * @param mixed $changes - * @param mixed $operator - * @param mixed $kvp + * @param array $changes + * @param array|array $kvp */ - private function addOperatorToChange(&$changes, $operator, $kvp) + private function addOperatorToChange(array &$changes, string $operator, array $kvp): void { if (!isset($changes[$operator]) || !is_array($changes[$operator])) { $changes[$operator] = []; } + foreach ($kvp as $key => $value) { if (isset($changes[$operator][$key])) { - $value = array_merge($value, $changes[$operator][$key]); + if (!is_array($changes[$operator][$key])) { + $changes[$operator][$key] = [$changes[$operator][$key]]; + } + + $changes[$operator][$key][] = $value; + } else { + $changes[$operator][$key] = $value; } - $changes[$operator][$key] = $value; } } /** - * @return array + * @return int[]|string[] */ - private function getAsyncOperations() + private function getAsyncOperations(): array { $types = []; foreach ($this->async as $op => $isAsync) { @@ -1336,9 +1270,9 @@ private function getAsyncOperations() } /** - * @return array + * @return int[]|string[] */ - private function getSyncOperations() + private function getSyncOperations(): array { $types = []; foreach ($this->async as $op => $isAsync) { diff --git a/src/mongo/delegates/Views.php b/src/mongo/delegates/Views.php index 73703381..9a2634f9 100644 --- a/src/mongo/delegates/Views.php +++ b/src/mongo/delegates/Views.php @@ -1,5 +1,7 @@ storeName = $storeName; $this->labeller = new Labeller(); @@ -36,20 +36,15 @@ public function __construct($storeName, Collection $collection, $defaultContext, $this->readPreference = $readPreference; } - /** - * @return string - */ - public function getOperationType() + public function getOperationType(): string { return OP_VIEWS; } /** * Receive update from subject. - * - * @param ImpactedSubject */ - public function update(ImpactedSubject $subject) + public function update(ImpactedSubject $subject): void { $resource = $subject->getResourceId(); $resourceUri = $resource[_ID_RESOURCE]; @@ -58,23 +53,18 @@ public function update(ImpactedSubject $subject) $this->generateViews([$resourceUri], $context); } - /** - * @return array - */ - public function getTypesInSpecifications() + public function getTypesInSpecifications(): array { return $this->config->getTypesInViewSpecifications($this->storeName, $this->getPodName()); } /** - * @param string $contextAlias - * - * @return array|mixed + * @return mixed[] */ - public function findImpactedComposites(array $resourcesAndPredicates, $contextAlias) + public function findImpactedComposites(array $resourcesAndPredicates, string $contextAlias): array { // This should never happen, but in the event that we have been passed an empty array or something - if (empty($resourcesAndPredicates)) { + if ($resourcesAndPredicates === []) { return []; } @@ -89,7 +79,7 @@ public function findImpactedComposites(array $resourcesAndPredicates, $contextAl // build $filter for queries to impact index $filter[] = [_ID_RESOURCE => $resourceAlias, _ID_CONTEXT => $contextAlias]; $rdfTypePredicates = array_intersect($predicates, $typeKeys); - if (!empty($rdfTypePredicates)) { + if ($rdfTypePredicates !== []) { $changedTypes[] = $resourceAlias; } } @@ -97,7 +87,7 @@ public function findImpactedComposites(array $resourcesAndPredicates, $contextAl // first re-gen views where resources appear in the impact index $query = ['value.' . _IMPACT_INDEX => ['$in' => $filter]]; - if (!empty($changedTypes)) { + if ($changedTypes !== []) { $query = ['$or' => [$query]]; foreach ($changedTypes as $resourceAlias) { $query['$or'][] = [ @@ -130,13 +120,7 @@ public function findImpactedComposites(array $resourcesAndPredicates, $contextAl return $affectedViews; } - /** - * @param string $storeName - * @param string $viewSpecId - * - * @return array|null - */ - public function getSpecification($storeName, $viewSpecId) + public function getSpecification(string $storeName, string $viewSpecId): ?array { return $this->config->getViewSpecification($storeName, $viewSpecId); } @@ -144,12 +128,9 @@ public function getSpecification($storeName, $viewSpecId) /** * Return all views, restricted by $filter conditions, for given $viewType. * - * @param array $filter - an array, keyed by predicate, to filter by - * @param mixed $viewType - * - * @return MongoGraph + * @param array $filter - an array, keyed by predicate, to filter by */ - public function getViews(array $filter, $viewType) + public function getViews(array $filter, string $viewType): MongoGraph { $query = ['_id.type' => $viewType]; foreach ($filter as $predicate => $object) { @@ -160,11 +141,13 @@ public function getViews(array $filter, $viewType) $values[] = ['value.' . _GRAPHS . '.' . $p => $o]; } } + $query[$predicate] = $values; } else { $query['value.' . _GRAPHS . '.' . $predicate] = $object; } } + $viewCollection = $this->getConfigInstance()->getCollectionForView($this->storeName, $viewType, $this->readPreference); return $this->fetchGraph($query, MONGO_VIEW, $viewCollection); @@ -172,14 +155,8 @@ public function getViews(array $filter, $viewType) /** * For given $resource, return the view of type $viewType. - * - * @param string|null $resource - * @param string $viewType - * @param string|null $context - * - * @return MongoGraph */ - public function getViewForResource($resource, $viewType, $context = null) + public function getViewForResource(?string $resource, string $viewType, ?string $context = null): MongoGraph { if (empty($resource)) { return new MongoGraph(); @@ -192,7 +169,7 @@ public function getViewForResource($resource, $viewType, $context = null) $viewCollection = $this->config->getCollectionForView($this->storeName, $viewType, $this->readPreference); $graph = $this->fetchGraph($query, MONGO_VIEW, $viewCollection); if ($graph->is_empty()) { - $this->getStat()->increment(MONGO_VIEW_CACHE_MISS . ".{$viewType}"); + $this->getStat()->increment(MONGO_VIEW_CACHE_MISS . ('.' . $viewType)); $viewSpec = $this->getConfigInstance()->getViewSpecification($this->storeName, $viewType); if ($viewSpec == null) { return new MongoGraph(); @@ -220,13 +197,8 @@ public function getViewForResource($resource, $viewType, $context = null) /** * For given $resources, return the views of type $viewType. - * - * @param string $viewType - * @param string|null $context - * - * @return MongoGraph */ - public function getViewForResources(array $resources, $viewType, $context = null) + public function getViewForResources(array $resources, string $viewType, ?string $context = null): MongoGraph { $contextAlias = $this->getContextAlias($context); @@ -241,7 +213,7 @@ public function getViewForResources(array $resources, $viewType, $context = null // account for missing subjects $returnedSubjects = $g->get_subjects(); $missingSubjects = array_diff($resources, $returnedSubjects); - if (!empty($missingSubjects)) { + if ($missingSubjects !== []) { $regrabResources = []; foreach ($missingSubjects as $missingSubject) { $viewSpec = $this->getConfigInstance()->getViewSpecification($this->storeName, $viewType); @@ -261,7 +233,7 @@ public function getViewForResources(array $resources, $viewType, $context = null $regrabResources[] = $missingSubject; } - if (!empty($regrabResources)) { + if ($regrabResources !== []) { // only try to regrab resources if there are any to regrab $cursorSize = 101; if (count($regrabResources) > 101) { @@ -278,11 +250,8 @@ public function getViewForResources(array $resources, $viewType, $context = null /** * Autodiscovers the multiple view specification that may be applicable for a given resource, and submits each for generation. - * - * @param array $resources - * @param string|null $context */ - public function generateViews($resources, $context = null) + public function generateViews(array $resources, ?string $context = null): void { $contextAlias = $this->getContextAlias($context); @@ -328,15 +297,9 @@ public function generateViews($resources, $context = null) /** * This method finds all the view specs for the given $rdfType and generates the views for the $resource one by one. * - * @param string $rdfType - * @param string|null $resource - * @param string|null $context - * - * @return mixed - * * @throws \Exception */ - public function generateViewsForResourcesOfType($rdfType, $resource = null, $context = null) + public function generateViewsForResourcesOfType(string $rdfType, ?string $resource = null, ?string $context = null): void { $rdfType = $this->labeller->qname_to_alias($rdfType); $rdfTypeAlias = $this->labeller->uri_to_alias($rdfType); @@ -346,14 +309,16 @@ public function generateViewsForResourcesOfType($rdfType, $resource = null, $con // check for rdfType and rdfTypeAlias if ( ($viewSpec['type'] == $rdfType || (is_array($viewSpec['type']) && in_array($rdfType, $viewSpec['type']))) - || ($viewSpec['type'] == $rdfTypeAlias || (is_array($viewSpec['type']) && in_array($rdfTypeAlias, $viewSpec['type'])))) { + || ($viewSpec['type'] == $rdfTypeAlias || (is_array($viewSpec['type']) && in_array($rdfTypeAlias, $viewSpec['type']))) + ) { $foundSpec = true; - $this->debugLog("Processing {$viewSpec['_id']}"); + $this->debugLog('Processing ' . $viewSpec['_id']); $this->generateView($key, $resource, $context); } } + if (!$foundSpec) { - $this->debugLog("Could not find any view specifications for {$resource} with resource type '{$rdfType}'"); + $this->debugLog(sprintf("Could not find any view specifications for %s with resource type '%s'", $resource, $rdfType)); return; } @@ -362,29 +327,32 @@ public function generateViewsForResourcesOfType($rdfType, $resource = null, $con /** * This method will delete all views where the _id.type of the viewmatches the specified $viewId. * - * @param string $viewId View spec ID - * @param UTCDateTime|null $timestamp Optional timestamp to delete all views that are older than + * @param string $viewId View spec ID + * @param int|UTCDateTime|null $timestamp Optional timestamp to delete all views that are older than * * @return int The number of views deleted */ - public function deleteViewsByViewId($viewId, $timestamp = null) + public function deleteViewsByViewId(string $viewId, $timestamp = null): int { $viewSpec = $this->getConfigInstance()->getViewSpecification($this->storeName, $viewId); if ($viewSpec == null) { - $this->debugLog("Could not find a view specification with viewId '{$viewId}'"); + $this->debugLog(sprintf("Could not find a view specification with viewId '%s'", $viewId)); - return; + return 0; } + $query = ['_id.type' => $viewId]; if ($timestamp) { if (!$timestamp instanceof UTCDateTime) { $timestamp = DateUtil::getMongoDate($timestamp); } + $query['$or'] = [ [\_CREATED_TS => ['$lt' => $timestamp]], [\_CREATED_TS => ['$exists' => false]], ]; } + $deleteResult = $this->getCollectionForViewSpec($viewId) ->deleteMany($query); @@ -394,24 +362,20 @@ public function deleteViewsByViewId($viewId, $timestamp = null) /** * Given a specific $viewId, generates a single view for the $resource. * - * @param string $viewId - * @param string|null $resource - * @param string|null $context * @param string|null $queueName Queue for background bulk generation * - * @return array - * * @throws ViewException */ - public function generateView($viewId, $resource = null, $context = null, $queueName = null) + public function generateView(string $viewId, ?string $resource = null, ?string $context = null, ?string $queueName = null): ?array { $contextAlias = $this->getContextAlias($context); $viewSpec = $this->getConfigInstance()->getViewSpecification($this->storeName, $viewId); if ($viewSpec == null) { - $this->debugLog("Could not find a view specification for {$resource} with viewId '{$viewId}'"); + $this->debugLog(sprintf("Could not find a view specification for %s with viewId '%s'", $resource, $viewId)); return null; } + $t = new Timer(); $t->start(); @@ -432,6 +396,7 @@ public function generateView($viewId, $resource = null, $context = null, $queueN $types[] = ['rdf:type.u' => $this->labeller->qname_to_alias($viewSpec['type'])]; $types[] = ['rdf:type.u' => $this->labeller->uri_to_alias($viewSpec['type'])]; } + $filter = ['$or' => $types]; if (isset($resource)) { $resourceAlias = $this->labeller->uri_to_alias($resource); @@ -452,6 +417,7 @@ public function generateView($viewId, $resource = null, $context = null, $queueN $jobOptions[ApplyOperation::TRACKING_KEY] = $jobGroup->getId()->__toString(); $jobGroup->setJobCount($count); } + foreach ($docs as $doc) { if ($queueName && !$resource) { $subject = new ImpactedSubject( @@ -503,7 +469,7 @@ public function generateView($viewId, $resource = null, $context = null, $queueN } } - if (!empty($subjects)) { + if ($subjects !== []) { $this->queueApplyJob($subjects, $queueName, $jobOptions); } @@ -512,8 +478,9 @@ public function generateView($viewId, $resource = null, $context = null, $queueN 'view' => $viewSpec['type'], 'duration' => $t->result(), 'filter' => $filter, - 'from' => $from]); - $this->getStat()->timer(MONGO_CREATE_VIEW . ".{$viewId}", $t->result()); + 'from' => $from, + ]); + $this->getStat()->timer(MONGO_CREATE_VIEW . ('.' . $viewId), $t->result()); $stat = ['count' => $count]; if (isset($jobOptions[ApplyOperation::TRACKING_KEY])) { @@ -526,29 +493,25 @@ public function generateView($viewId, $resource = null, $context = null, $queueN /** * Count the number of documents in the spec that match $filters. * - * @param string $viewSpec View spec ID - * @param array $filters Query filters to get count on - * - * @return int + * @param string $viewSpec View spec ID + * @param array $filters Query filters to get count on */ - public function count($viewSpec, array $filters = []) + public function count(string $viewSpec, array $filters = []): int { $filters['_id.type'] = $viewSpec; return $this->getCollectionForViewSpec($viewSpec)->count($filters); } + protected function getExpirySecFromNow(int $secs): int + { + return time() + $secs; + } + /** * Joins data to $dest from $source according to specification in $joins, or queries DB if data is not available in $source. - * - * @param bool $buildImpactIndex - * @param mixed $source - * @param mixed $joins - * @param mixed $dest - * @param mixed $from - * @param mixed $contextAlias */ - protected function doJoins($source, $joins, &$dest, $from, $contextAlias, $buildImpactIndex = true) + protected function doJoins(array $source, array $joins, array &$dest, string $from, string $contextAlias, bool $buildImpactIndex = true): void { // expand sequences before doing any joins... $this->expandSequence($joins, $source); @@ -577,6 +540,7 @@ protected function doJoins($source, $joins, &$dest, $from, $contextAlias, $build if (isset($ruleset['maxJoins']) && !$joinsPushed < $ruleset['maxJoins']) { break; // maxJoins reached } + $joinUris[] = [_ID_RESOURCE => $v[VALUE_URI], _ID_CONTEXT => $contextAlias]; $joinsPushed++; } @@ -599,19 +563,22 @@ protected function doJoins($source, $joins, &$dest, $from, $contextAlias, $build if (isset($ruleset['condition'])) { $ruleset['condition']['._id'] = $linkMatch['_id']; } + if (!(isset($ruleset['condition']) && $collection->count($ruleset['condition']) == 0)) { // make sure any sequences are expanded before extracting properties if (isset($ruleset['joins'])) { $this->expandSequence($ruleset['joins'], $linkMatch); } + if (isset($ruleset['filter'])) { foreach ($ruleset['filter'] as $filterPredicate => $filter) { foreach ($filter as $filterType => $filterMatch) { if (isset($linkMatch[$filterPredicate])) { foreach ($linkMatch[$filterPredicate] as $linkMatchType => $linkMatchValues) { - if (is_array($linkMatchValues) == false) { + if (is_array($linkMatchValues) === false) { $linkMatchValues = [$linkMatchType => $linkMatchValues]; } + foreach ($linkMatchValues as $linkMatchType => $linkMatchValue) { if ($this->matchesFilter($linkMatchType, $linkMatchValue, $filterType, $filterMatch)) { $dest[_GRAPHS][] = $this->extractProperties($linkMatch, $ruleset, $from); @@ -631,10 +598,9 @@ protected function doJoins($source, $joins, &$dest, $from, $contextAlias, $build } } } - if (count($recursiveJoins) > 0) { - foreach ($recursiveJoins as $r) { - $this->doJoins($r['data'], $r['ruleset'], $dest, $from, $contextAlias, $buildImpactIndex); - } + + foreach ($recursiveJoins as $r) { + $this->doJoins($r['data'], $r['ruleset'], $dest, $from, $contextAlias, $buildImpactIndex); } } } @@ -642,36 +608,24 @@ protected function doJoins($source, $joins, &$dest, $from, $contextAlias, $build /** * Check to see if a linkMatch matches a filter. - * - * @param string $linkMatchType - * @param string $linkMatchValue - * @param string $filterType - * @param string $filterMatch - * - * @return bool */ - protected function matchesFilter($linkMatchType, $linkMatchValue, $filterType, $filterMatch) + protected function matchesFilter(string $linkMatchType, string $linkMatchValue, string $filterType, string $filterMatch): bool { - if ($linkMatchType === $filterType) { - if ($linkMatchValue === $filterMatch || $this->labeller->uri_to_alias($linkMatchValue) === $filterMatch) { - return true; - } + if ($linkMatchType !== $filterType) { + return false; } - return false; + return $linkMatchValue === $filterMatch || $this->labeller->uri_to_alias($linkMatchValue) === $filterMatch; } /** * Returns a document with properties extracted from $source, according to $viewSpec. Useful for partial representations * of CBDs in a view. * - * @param mixed $source - * @param mixed $viewSpec - * @param mixed $from - * - * @return array + * @param array $source + * @param array $viewSpec */ - protected function extractProperties($source, $viewSpec, $from) + protected function extractProperties(array $source, array $viewSpec, string $from): array { $obj = []; if (isset($viewSpec['include'])) { @@ -680,23 +634,23 @@ protected function extractProperties($source, $viewSpec, $from) if (isset($source[$p])) { $obj[$p] = $source[$p]; } - if ($p === INCLUDE_RDF_SEQUENCE) { - if ($source['rdf:type']) { - foreach ($source['rdf:type'] as $u => $t) { - if (is_array($t) == false) { - $t = [$u => $t]; - } - foreach ($t as $typeOfType => $type) { - if ($typeOfType === 'u' && $type === 'rdf:Seq') { - $seqNumber = 1; - $found = true; - while ($found) { - if (isset($source['rdf:_' . $seqNumber])) { - $obj['rdf:_' . $seqNumber] = $source['rdf:_' . $seqNumber]; - $seqNumber++; - } else { - $found = false; - } + + if ($p === INCLUDE_RDF_SEQUENCE && $source['rdf:type']) { + foreach ($source['rdf:type'] as $u => $t) { + if (is_array($t) === false) { + $t = [$u => $t]; + } + + foreach ($t as $typeOfType => $type) { + if ($typeOfType === 'u' && $type === 'rdf:Seq') { + $seqNumber = 1; + $found = true; + while ($found) { + if (isset($source['rdf:_' . $seqNumber])) { + $obj['rdf:_' . $seqNumber] = $source['rdf:_' . $seqNumber]; + $seqNumber++; + } else { + $found = false; } } } @@ -704,19 +658,22 @@ protected function extractProperties($source, $viewSpec, $from) } } } + if (isset($viewSpec['joins'])) { foreach ($viewSpec['joins'] as $p => $join) { if (isset($join['maxJoins'])) { // todo: refactor with below (extract method) // only include up to maxJoins for ($i = 0; $i < $join['maxJoins']; $i++) { - if (isset($source[$p]) && (isset($source[$p][VALUE_URI]) || isset($source[$p][VALUE_LITERAL])) && $i == 0) { // cater for source with only one val + if (isset($source[$p]) && (isset($source[$p][VALUE_URI]) || isset($source[$p][VALUE_LITERAL])) && $i === 0) { // cater for source with only one val $obj[$p] = $source[$p]; } + if (isset($source[$p], $source[$p][$i])) { if (!isset($obj[$p])) { $obj[$p] = []; } + $obj[$p][] = $source[$p][$i]; } } @@ -731,13 +688,15 @@ protected function extractProperties($source, $viewSpec, $from) // todo: refactor with above (extract method) // only include up to maxJoins for ($i = 0; $i < $viewSpec['joins'][$p]['maxJoins']; $i++) { - if ($val && (isset($val[VALUE_URI]) || isset($val[VALUE_LITERAL])) && $i == 0) { // cater for source with only one val + if ($val && (isset($val[VALUE_URI]) || isset($val[VALUE_LITERAL])) && $i === 0) { // cater for source with only one val $obj[$p] = $val; } + if ($val && isset($val[$i])) { if (!$obj[$p]) { $obj[$p] = []; } + $obj[$p][] = $val[$i]; } } @@ -769,6 +728,7 @@ protected function extractProperties($source, $viewSpec, $from) $count = count($source[$c['property']]); } } + $obj[$predicate] = [VALUE_LITERAL => (string) $count]; } } @@ -777,24 +737,12 @@ protected function extractProperties($source, $viewSpec, $from) return $obj; } - /** - * @param string $viewSpecId - * - * @return Collection - */ - protected function getCollectionForViewSpec($viewSpecId) + protected function getCollectionForViewSpec(string $viewSpecId): Collection { return $this->getConfigInstance()->getCollectionForView($this->storeName, $viewSpecId); } - /** - * @param array|string $resourceUriOrArray - * @param string $context - * @param string $viewType - * - * @return array - */ - private function createTripodViewIdsFromResourceUris($resourceUriOrArray, $context, $viewType) + private function createTripodViewIdsFromResourceUris(array $resourceUriOrArray, ?string $context, string $viewType): array { $contextAlias = $this->getContextAlias($context); $ret = []; @@ -806,19 +754,12 @@ private function createTripodViewIdsFromResourceUris($resourceUriOrArray, $conte } /** - * @param string $viewSpec + * @param array{from: string}|null $viewSpec * * @return string */ - private function getFromCollectionForViewSpec($viewSpec) + private function getFromCollectionForViewSpec(?array $viewSpec) { - $from = null; - if (isset($viewSpec['from'])) { - $from = $viewSpec['from']; - } else { - $from = $this->podName; - } - - return $from; + return $viewSpec['from'] ?? $this->podName; } } diff --git a/src/mongo/documents/Tables.php b/src/mongo/documents/Tables.php index d4017774..3e749e5a 100644 --- a/src/mongo/documents/Tables.php +++ b/src/mongo/documents/Tables.php @@ -1,5 +1,7 @@ exchangeArray($this->toTableRow($data)); } @@ -24,7 +26,7 @@ public function bsonUnserialize(array $data) /** * Models the table row from the source data. * - * @param array $doc Database document + * @param array $doc Database document * * @return array */ diff --git a/src/mongo/jobs/ApplyOperation.php b/src/mongo/jobs/ApplyOperation.php index a21ab2ff..b367fd34 100644 --- a/src/mongo/jobs/ApplyOperation.php +++ b/src/mongo/jobs/ApplyOperation.php @@ -1,5 +1,7 @@ getStat()->increment( MONGO_QUEUE_APPLY_OPERATION_JOB . '.' . SUBJECT_COUNT, @@ -51,8 +55,7 @@ public function perform() $jobGroup = $this->getJobGroup($subject['storeName'], $this->args[self::TRACKING_KEY]); $jobCount = $jobGroup->incrementJobCount(-1); if ($jobCount <= 0) { - // @todo Replace this with ObjectId->getTimestamp() if we upgrade Mongo driver to 1.2 - $timestamp = new UTCDateTime(hexdec(substr($jobGroup->getId(), 0, 8)) * 1000); + $timestamp = new UTCDateTime($jobGroup->getId()->getTimestamp() * 1000); $tripod = $this->getTripod($subject['storeName'], $subject['podName']); $count = 0; foreach ($subject['specTypes'] as $specId) { @@ -74,9 +77,10 @@ public function perform() break; } } + $this->infoLog( '[JobGroupId ' . $jobGroup->getId()->__toString() . '] composite cleanup for ' - . $subject['operation'] . ' removed ' . $count . ' stale composite documents' + . $subject['operation'] . ' removed ' . $count . ' stale composite documents' ); } } @@ -85,10 +89,8 @@ public function perform() /** * @param ImpactedSubject[] $subjects - * @param string|null $queueName - * @param array $otherData */ - public function createJob(array $subjects, $queueName = null, $otherData = []) + public function createJob(array $subjects, ?string $queueName = null, array $otherData = []): void { $configInstance = $this->getConfigInstance(); if (!$queueName) { @@ -99,9 +101,7 @@ public function createJob(array $subjects, $queueName = null, $otherData = []) $data = [ self::SUBJECTS_KEY => array_map( - function (ImpactedSubject $subject) { - return $subject->toArray(); - }, + fn (ImpactedSubject $subject): array => $subject->toArray(), $subjects ), ]; @@ -116,20 +116,16 @@ function (ImpactedSubject $subject) { /** * Stat string for successful job timer. - * - * @return string */ - protected function getStatTimerSuccessKey() + protected function getStatTimerSuccessKey(): string { return MONGO_QUEUE_APPLY_OPERATION_SUCCESS; } /** * Stat string for failed job increment. - * - * @return string */ - protected function getStatFailureIncrementKey() + protected function getStatFailureIncrementKey(): string { return MONGO_QUEUE_APPLY_OPERATION_FAIL; } @@ -137,9 +133,9 @@ protected function getStatFailureIncrementKey() /** * For mocking. * - * @return ImpactedSubject + * @param array $args */ - protected function createImpactedSubject(array $args) + protected function createImpactedSubject(array $args): ImpactedSubject { return new ImpactedSubject( $args['resourceId'], @@ -155,20 +151,16 @@ protected function createImpactedSubject(array $args) * * @param string $storeName Tripod store (database) name * @param ObjectId|string $trackingKey JobGroup ID - * - * @return JobGroup */ - protected function getJobGroup($storeName, $trackingKey) + protected function getJobGroup(string $storeName, $trackingKey): JobGroup { return new JobGroup($storeName, $trackingKey); } /** * For mocking. - * - * @return MongoSearchProvider */ - protected function getSearchProvider(Driver $tripod) + protected function getSearchProvider(Driver $tripod): MongoSearchProvider { return new MongoSearchProvider($tripod); } diff --git a/src/mongo/jobs/DiscoverImpactedSubjects.php b/src/mongo/jobs/DiscoverImpactedSubjects.php index 649876ed..1bacc38b 100644 --- a/src/mongo/jobs/DiscoverImpactedSubjects.php +++ b/src/mongo/jobs/DiscoverImpactedSubjects.php @@ -1,5 +1,7 @@ */ protected $subjectsGroupedByQueue = []; protected $configRequired = true; - protected $subjectCount; + protected int $subjectCount; protected $mandatoryArgs = [ self::STORE_NAME_KEY, @@ -36,7 +39,7 @@ class DiscoverImpactedSubjects extends JobBase self::CONTEXT_ALIAS_KEY, ]; - public function tearDown() + public function tearDown(): void { parent::tearDown(); $this->getStat()->increment(MONGO_QUEUE_DISCOVER_JOB . '.' . SUBJECT_COUNT, $this->subjectCount); @@ -47,7 +50,7 @@ public function tearDown() * * @throws \Exception */ - public function perform() + public function perform(): void { $tripod = $this->getTripod( $this->args[self::STORE_NAME_KEY], @@ -74,12 +77,9 @@ public function perform() $this->subjectCount++; $subjectTimer = new Timer(); $subjectTimer->start(); - if (isset($this->args[self::QUEUE_KEY]) || count($subject->getSpecTypes()) == 0) { - if (isset($this->args[self::QUEUE_KEY])) { - $queueName = $this->args[self::QUEUE_KEY]; - } else { - $queueName = $configInstance::getApplyQueueName(); - } + if (isset($this->args[self::QUEUE_KEY]) || count($subject->getSpecTypes()) === 0) { + $queueName = $this->args[self::QUEUE_KEY] ?? $configInstance::getApplyQueueName(); + $this->addSubjectToQueue($subject, $queueName); } else { $specsGroupedByQueue = []; @@ -111,15 +111,19 @@ public function perform() break; } + if (!$spec || !isset($spec['queue'])) { if (!$spec) { $spec = []; } + $spec['queue'] = $configInstance::getApplyQueueName(); } + if (!isset($specsGroupedByQueue[$spec['queue']])) { $specsGroupedByQueue[$spec['queue']] = []; } + $specsGroupedByQueue[$spec['queue']][] = $specType; } @@ -135,24 +139,24 @@ public function perform() $this->addSubjectToQueue($queuedSubject, $queueName); } } + $subjectTimer->stop(); // stat time taken to discover impacted subjects for the given subject of change $this->getStat()->timer(MONGO_QUEUE_DISCOVER_SUBJECT, $subjectTimer->result()); } - if (!empty($this->subjectsGroupedByQueue)) { + + if ($this->subjectsGroupedByQueue !== []) { foreach ($this->subjectsGroupedByQueue as $queueName => $subjects) { $this->getApplyOperation()->createJob($subjects, $queueName, $this->getTripodOptions()); } + $this->subjectsGroupedByQueue = []; } } } } - /** - * @param string|null $queueName - */ - public function createJob(array $data, $queueName = null) + public function createJob(array $data, ?string $queueName = null): void { $configInstance = $this->getConfigInstance(); if (!$queueName) { @@ -160,48 +164,41 @@ public function createJob(array $data, $queueName = null) } elseif (strpos($queueName, $configInstance::getDiscoverQueueName()) === false) { $queueName = $configInstance::getDiscoverQueueName() . '::' . $queueName; } + $this->submitJob($queueName, get_class($this), array_merge($data, $this->generateConfigJobArgs())); } /** * Stat string for successful job timer. - * - * @return string */ - protected function getStatTimerSuccessKey() + protected function getStatTimerSuccessKey(): string { return MONGO_QUEUE_DISCOVER_SUCCESS; } /** * Stat string for failed job increment. - * - * @return string */ - protected function getStatFailureIncrementKey() + protected function getStatFailureIncrementKey(): string { return MONGO_QUEUE_DISCOVER_FAIL; } - /** - * @param string $queueName - */ - protected function addSubjectToQueue(ImpactedSubject $subject, $queueName) + protected function addSubjectToQueue(ImpactedSubject $subject, string $queueName): void { if (!array_key_exists($queueName, $this->subjectsGroupedByQueue)) { $this->subjectsGroupedByQueue[$queueName] = []; } + $this->subjectsGroupedByQueue[$queueName][] = $subject; } /** * For mocking. - * - * @return ApplyOperation */ - protected function getApplyOperation() + protected function getApplyOperation(): ApplyOperation { - if (!isset($this->applyOperation)) { + if ($this->applyOperation === null) { $this->applyOperation = new ApplyOperation(); } diff --git a/src/mongo/jobs/EnsureIndexes.php b/src/mongo/jobs/EnsureIndexes.php index 19093a0c..6425fd4c 100644 --- a/src/mongo/jobs/EnsureIndexes.php +++ b/src/mongo/jobs/EnsureIndexes.php @@ -1,5 +1,7 @@ debugLog('Ensuring indexes for tenant=' . $this->args[self::STORENAME_KEY] . ', reindex=' . $this->args[self::REINDEX_KEY] . ', background=' . $this->args[self::BACKGROUND_KEY]); @@ -31,13 +36,8 @@ public function perform() /** * This method is use to schedule an EnsureIndexes job. - * - * @param string $storeName - * @param booelan $reindex - * @param string $queueName - * @param mixed $background */ - public function createJob($storeName, $reindex, $background, $queueName = null) + public function createJob(string $storeName, bool $reindex, bool $background, ?string $queueName = null): void { $configInstance = $this->getConfigInstance(); if (!$queueName) { @@ -57,28 +57,21 @@ public function createJob($storeName, $reindex, $background, $queueName = null) /** * Stat string for successful job timer. - * - * @return string */ - protected function getStatTimerSuccessKey() + protected function getStatTimerSuccessKey(): string { return MONGO_QUEUE_ENSURE_INDEXES_SUCCESS; } /** * Stat string for failed job increment. - * - * @return string */ - protected function getStatFailureIncrementKey() + protected function getStatFailureIncrementKey(): string { return MONGO_QUEUE_ENSURE_INDEXES_FAIL; } - /** - * @return IndexUtils - */ - protected function getIndexUtils() + protected function getIndexUtils(): IndexUtils { return new IndexUtils(); } diff --git a/src/mongo/providers/ISearchProvider.php b/src/mongo/providers/ISearchProvider.php index 7478ec98..2b063adb 100644 --- a/src/mongo/providers/ISearchProvider.php +++ b/src/mongo/providers/ISearchProvider.php @@ -1,5 +1,7 @@ tripod = $tripod; $this->storeName = $tripod->getStoreName(); $this->labeller = new Labeller(); - $this->config = \Tripod\Config::getInstance(); + $this->config = Config::getInstance(); } /** @@ -48,11 +214,9 @@ public function __construct(Driver $tripod) * * @param array $document the document to index * - * @return mixed - * * @throws SearchException if there was an error indexing the document */ - public function indexDocument($document) + public function indexDocument(array $document): void { if (isset($document['_id']['type'])) { $collection = $this->config->getCollectionForSearchDocument($this->storeName, $document['_id']['type']); @@ -74,15 +238,11 @@ public function indexDocument($document) * Removes a single document from the search index based on the specified resource and context and spec id. * If spec id is not specified this method will delete all search documents that match the resource and context. * - * @param string $resource - * @param string $context * @param array|string|null $specId * - * @return mixed - * * @throws SearchException if there was an error removing the document */ - public function deleteDocument($resource, $context, $specId = []) + public function deleteDocument(string $resource, string $context, $specId = []): void { $query = [_ID_KEY . '.' . _ID_RESOURCE => $this->labeller->uri_to_alias($resource), _ID_KEY . '.' . _ID_CONTEXT => $context]; @@ -94,18 +254,21 @@ public function deleteDocument($resource, $context, $specId = []) if (!in_array($specId, $specTypes)) { return; } + $query[_ID_KEY][_ID_TYPE] = $specId; $searchTypes[] = $specId; } elseif (is_array($specId)) { // Only filter on search document spec types $specId = array_intersect($specTypes, $specId); - if (empty($specId)) { + if ($specId === []) { return; } + $query[_ID_KEY . '.' . _ID_TYPE] = ['$in' => array_values($specId)]; $searchTypes = $specId; } } + foreach ($this->config->getCollectionsForSearch($this->storeName, $searchTypes) as $collection) { $collection->deleteMany($query); } @@ -118,11 +281,9 @@ public function deleteDocument($resource, $context, $specId = []) * Returns the ids of all documents that contain and impact index entry * matching the resource and context specified. * - * @param string $context - * * @return array the ids of search documents that had matching entries in their impact index */ - public function findImpactedDocuments(array $resourcesAndPredicates, $context) + public function findImpactedDocuments(array $resourcesAndPredicates, string $context): array { $contextAlias = $this->labeller->uri_to_alias($context); @@ -142,7 +303,7 @@ public function findImpactedDocuments(array $resourcesAndPredicates, $context) $id = [_ID_RESOURCE => $resourceAlias, _ID_CONTEXT => $contextAlias]; // If we don't have a working config or there are no predicates listed, remove all // rows associated with the resource in all search types - if (empty($specPredicates) || empty($resourcePredicates)) { + if ($specPredicates === [] || empty($resourcePredicates)) { // build $filter for queries to impact index $resourceFilters[] = $id; } else { @@ -152,6 +313,7 @@ public function findImpactedDocuments(array $resourcesAndPredicates, $context) if (!isset($searchDocFilters[$searchDocType])) { $searchDocFilters[$searchDocType] = []; } + // build $filter for queries to impact index $searchDocFilters[$searchDocType][] = $id; } @@ -160,7 +322,7 @@ public function findImpactedDocuments(array $resourcesAndPredicates, $context) } $searchTypes = []; - if (empty($searchDocFilters) && !empty($resourceFilters)) { + if ($searchDocFilters === [] && $resourceFilters !== []) { $query = [_IMPACT_INDEX => ['$in' => $resourceFilters]]; } else { $query = []; @@ -170,7 +332,7 @@ public function findImpactedDocuments(array $resourcesAndPredicates, $context) $searchTypes[] = $searchDocType; } - if (!empty($resourceFilters)) { + if ($resourceFilters !== []) { $query[] = [_IMPACT_INDEX => ['$in' => $resourceFilters]]; } @@ -180,7 +342,8 @@ public function findImpactedDocuments(array $resourcesAndPredicates, $context) $query = ['$or' => $query]; } } - if (empty($query)) { + + if ($query === []) { return []; } @@ -196,63 +359,57 @@ public function findImpactedDocuments(array $resourcesAndPredicates, $context) } /** - * @param string $q - * @param string $type - * @param array $indices - * @param array $fields - * @param int $limit - * @param int $offset - * - * @return array|mixed - * * @throws SearchException */ - public function search($q, $type, $indices = [], $fields = [], $limit = 10, $offset = 0) + public function search(string $query, string $type, array $indices = [], array $fields = [], int $limit = 10, int $offset = 0): array { - if (empty($q)) { + if (empty($query)) { throw new SearchException('You must specify a query'); } + if (empty($type)) { throw new SearchException('You must specify the search document type to restrict the query to'); } + if (empty($indices)) { throw new SearchException('You must specify at least one index from the search document specification to query against'); } + if (empty($fields)) { throw new SearchException('You must specify at least one field from the search document specification to return'); } - if (!is_numeric($limit) || $limit < 0) { + if ($limit < 0) { throw new SearchException('Value for limit must be a positive number'); } - if (!is_numeric($offset) || $offset < 0) { + if ($offset < 0) { throw new SearchException('Value for offset must be a positive number'); } - $original_terms = explode(' ', trim(strtolower($q))); + $original_terms = explode(' ', trim(strtolower($query))); $terms = array_values(array_diff($original_terms, $this->stopWords)); // todo: this means if all the words entered were stop words, then use the orginal terms rather than do nothing! - if (empty($terms)) { + if ($terms === []) { $terms = $original_terms; } $regexes = []; foreach ($terms as $t) { - $regexes[] = new Regex("{$t}", ''); + $regexes[] = new Regex($t, ''); } - $query = []; - $query['_id.type'] = $type; + $filter = []; + $filter['_id.type'] = $type; - if (count($indices) == 1) { + if (count($indices) === 1) { $searchIndex = $indices[0]; - $query[$searchIndex] = ['$all' => $regexes]; + $filter[$searchIndex] = ['$all' => $regexes]; } else { - $query['$or'] = []; + $filter['$or'] = []; foreach ($indices as $searchIndex) { - $query['$or'][] = ["{$searchIndex}" => ['$all' => $regexes]]; + $filter['$or'][] = [$searchIndex => ['$all' => $regexes]]; } } @@ -260,13 +417,16 @@ public function search($q, $type, $indices = [], $fields = [], $limit = 10, $off foreach ($fields as $field) { $fieldsToReturn[$field] = 1; } + $searchTimer = new Timer(); $searchTimer->start(); + $cursor = $this->config->getCollectionForSearchDocument($this->storeName, $type) - ->find($query, [ + ->find($filter, [ 'projection' => $fieldsToReturn, 'limit' => $limit, - 'skip' => $offset]); + 'skip' => $offset, + ]); $searchResults = []; $searchResults['head'] = []; @@ -274,11 +434,11 @@ public function search($q, $type, $indices = [], $fields = [], $limit = 10, $off $searchResults['head']['limit'] = $limit; $searchResults['head']['offset'] = $offset; $searchResults['head']['duration'] = ''; - $searchResults['head']['query'] = $q; + $searchResults['head']['query'] = $query; $searchResults['head']['query_terms_used'] = $terms; $searchResults['results'] = []; - $count = $this->config->getCollectionForSearchDocument($this->storeName, $type)->count($query); + $count = $this->config->getCollectionForSearchDocument($this->storeName, $type)->count($filter); if ($count > 0) { $searchResults['head']['count'] = $count; @@ -289,12 +449,9 @@ public function search($q, $type, $indices = [], $fields = [], $limit = 10, $off if (count($fields) > 1) { $r = []; foreach ($fields as $field) { - if (isset($result[$field])) { - $r[$field] = $result[$field]; - } else { - $r[$field] = ''; - } + $r[$field] = $result[$field] ?? ''; } + $searchResults['results'][] = $r; } else { $searchResults['results'][] = $result[$fields[0]]; @@ -303,16 +460,14 @@ public function search($q, $type, $indices = [], $fields = [], $limit = 10, $off } else { $searchResults['head']['count'] = 0; } + $searchTimer->stop(); $searchResults['head']['duration'] = $searchTimer->result() . ' ms'; return $searchResults; } - /** - * @return string - */ - public function getSearchCollectionName() + public function getSearchCollectionName(): string { return SEARCH_INDEX_COLLECTION; } @@ -322,29 +477,32 @@ public function getSearchCollectionName() * Here search type id represents to id from, mongo tripod config, that is converted to _id.type in SEARCH_INDEX_COLLECTION * If type id is not specified this method will throw an exception. * - * @param string $typeId Search type id - * @param UTCDateTime|null $timestamp Optional timestamp to delete all search docs that are older than + * @param string $typeId Search type id + * @param int|UTCDateTime|null $timestamp Optional timestamp to delete all search docs that are older than * * @return int The number of search documents deleted * * @throws \Tripod\Exceptions\Exception if there was an error performing the operation */ - public function deleteSearchDocumentsByTypeId($typeId, $timestamp = null) + public function deleteSearchDocumentsByTypeId(string $typeId, $timestamp = null): int { $searchSpec = $this->getSearchDocumentSpecification($typeId); if ($searchSpec == null) { - throw new SearchException("Could not find a search specification for {$typeId}"); + throw new SearchException('Could not find a search specification for ' . $typeId); } + $query = ['_id.type' => $typeId]; if ($timestamp) { if (!$timestamp instanceof UTCDateTime) { $timestamp = new UTCDateTime($timestamp); } + $query['$or'] = [ [\_CREATED_TS => ['$lt' => $timestamp]], [\_CREATED_TS => ['$exists' => false]], ]; } + $deleteResponse = $this->getCollectionForSearchSpec($typeId) ->deleteMany($query); @@ -354,12 +512,12 @@ public function deleteSearchDocumentsByTypeId($typeId, $timestamp = null) /** * Count the number of documents in the spec that match $filters. * - * @param string $searchSpec Search spec ID - * @param array $filters Query filters to get count on + * @param string $searchSpec Search spec ID + * @param array $filters Query filters to get count on * * @return int */ - public function count($searchSpec, array $filters = []) + public function count(string $searchSpec, array $filters = []) { $filters['_id.type'] = $searchSpec; @@ -368,12 +526,8 @@ public function count($searchSpec, array $filters = []) /** * Returns the search document specification for the supplied type. - * - * @param string $typeId - * - * @return array|null */ - protected function getSearchDocumentSpecification($typeId) + protected function getSearchDocumentSpecification(string $typeId): ?array { return $this->config->getSearchDocumentSpecification($this->storeName, $typeId); } @@ -382,10 +536,8 @@ protected function getSearchDocumentSpecification($typeId) * For mocking. * * @param string $searchSpecId Search spec ID - * - * @return Collection */ - protected function getCollectionForSearchSpec($searchSpecId) + protected function getCollectionForSearchSpec(string $searchSpecId): Collection { return $this->config->getCollectionForSearchDocument($this->storeName, $searchSpecId); } diff --git a/src/mongo/serializers/NQuadSerializer.php b/src/mongo/serializers/NQuadSerializer.php index 4e10071d..4126de59 100644 --- a/src/mongo/serializers/NQuadSerializer.php +++ b/src/mongo/serializers/NQuadSerializer.php @@ -1,5 +1,7 @@ esc_chars = []; $this->raw = 0; @@ -19,29 +22,30 @@ public function ___construct() /** * @param array|string $v - * - * @return string */ - public function getTerm($v) + public function getTerm($v): string { if (!is_array($v)) { if (preg_match('/^\_\:/', $v)) { return $v; } + if (preg_match('/^[a-z0-9]+\:[^\s\"]*$/is', $v)) { return '<' . htmlspecialchars($this->escape($v)) . '>'; } return $this->getTerm(['type' => 'literal', 'value' => $v]); } + if ($v['type'] != 'literal') { return $this->getTerm($v['value']); } + // literal $quot = '"'; if ($this->raw && preg_match('/\"/', $v['value'])) { $quot = "'"; - if (preg_match('/\'/', $v['value'])) { + if (preg_match("/'/", $v['value'])) { $quot = '"""'; if (preg_match('/\"\"\"/', $v['value']) || preg_match('/\"$/', $v['value']) || preg_match('/^\"/', $v['value'])) { $quot = "'''"; @@ -51,7 +55,8 @@ public function getTerm($v) } } } - if ($this->raw && (strlen($quot) == 1) && preg_match('/[\x0d\x0a]/', $v['value'])) { + + if ($this->raw && (strlen($quot) === 1) && preg_match('/[\x0d\x0a]/', $v['value'])) { $quot = $quot . $quot . $quot; } @@ -63,13 +68,7 @@ public function getTerm($v) return $quot . $escaped . $quot . $suffix; } - /** - * @param array $index - * @param string $context - * - * @return string - */ - public function getSerializedIndex($index, $context) + public function getSerializedIndex(array $index, ?string $context): string { $r = ''; $nl = "\n"; @@ -87,13 +86,15 @@ public function getSerializedIndex($index, $context) if (!is_array($os)) { // single literal o $os = [['value' => $os, 'type' => 'literal']]; } + foreach ($os as $o) { $o = $this->getTerm($o); - $r .= $r ? $nl : ''; + $r .= $r !== '' && $r !== '0' ? $nl : ''; $r .= $s . ' ' . $p . ' ' . $o; if ($context != null) { $r .= ' <' . $context . '>'; } + $r .= ' .'; } } @@ -102,15 +103,12 @@ public function getSerializedIndex($index, $context) return $r . $nl; } - /** - * @param string $v - * - * @return string - */ - public function escape($v) + public function escape(string $v): string { $r = ''; - $v = (strpos(utf8_decode(str_replace('?', '', $v)), '?') === false) ? utf8_decode($v) : $v; + $v = (strpos(mb_convert_encoding(str_replace('?', '', $v), 'ISO-8859-1', 'UTF-8'), '?') === false) + ? mb_convert_encoding($v, 'ISO-8859-1', 'UTF-8') + : $v; if ($this->raw) { return $v; } @@ -120,20 +118,16 @@ public function escape($v) if (!isset($this->esc_chars[$c])) { $this->esc_chars[$c] = $this->getEscapedChar($c, $this->getCharNo($c)); } + $r .= $this->esc_chars[$c]; } return $r; } - /** - * @param string $c - * - * @return int - */ - public function getCharNo($c) + public function getCharNo(string $c): int { - $c_utf = utf8_encode($c); + $c_utf = mb_convert_encoding($c, 'UTF-8', 'ISO-8859-1'); $bl = strlen($c_utf); // binary length $r = 0; @@ -162,50 +156,68 @@ public function getCharNo($c) return $r; } - /** - * @param string $c - * @param int $no - * - * @return string - */ - public function getEscapedChar($c, $no) + public function getEscapedChar(string $c, int $no): string { // see http://www.w3.org/TR/rdf-testcases/#ntrip_strings if ($no < 9) { return '\u' . sprintf('%04X', $no); - } // #x0-#x8 (0-8) + } + + // #x0-#x8 (0-8) if ($no == 9) { return '\t'; - } // #x9 (9) + } + + // #x9 (9) if ($no == 10) { return '\n'; - } // #xA (10) + } + + // #xA (10) if ($no < 13) { return '\u' . sprintf('%04X', $no); - } // #xB-#xC (11-12) + } + + // #xB-#xC (11-12) if ($no == 13) { return '\r'; - } // #xD (13) + } + + // #xD (13) if ($no < 32) { return '\u' . sprintf('%04X', $no); - } // #xE-#x1F (14-31) + } + + // #xE-#x1F (14-31) if ($no < 34) { return $c; - } // #x20-#x21 (32-33) + } + + // #x20-#x21 (32-33) if ($no == 34) { return '\"'; - } // #x22 (34) + } + + // #x22 (34) if ($no < 92) { return $c; - } // #x23-#x5B (35-91) + } + + // #x23-#x5B (35-91) if ($no == 92) { return '\\\\'; - } // #x5C (92) + } + + // #x5C (92) if ($no < 127) { return $c; - } // #x5D-#x7E (93-126) + } + + // #x5D-#x7E (93-126) if ($no < 65536) { return '\u' . sprintf('%04X', $no); - } // #x7F-#xFFFF (128-65535) + } + + // #x7F-#xFFFF (128-65535) if ($no < 1114112) { return '\U' . sprintf('%08X', $no); } // #x10000-#x10FFFF (65536-1114111) diff --git a/src/mongo/util/DateUtil.php b/src/mongo/util/DateUtil.php index 57858d5a..9b18ac94 100644 --- a/src/mongo/util/DateUtil.php +++ b/src/mongo/util/DateUtil.php @@ -1,5 +1,7 @@ getConfig(); $dbs = ($storeName == null) ? $config->getDbs() : [$storeName]; @@ -67,88 +69,81 @@ public function ensureIndexes($reindex = false, $storeName = null, $background = // Index views foreach ($config->getViewSpecifications($storeName) as $viewId => $spec) { $collection = $config->getCollectionForView($storeName, $viewId); - if ($collection) { - $indexes = [ - [_ID_KEY . '.' . _ID_RESOURCE => 1, _ID_KEY . '.' . _ID_CONTEXT => 1, _ID_KEY . '.' . _ID_TYPE => 1], - [_ID_KEY . '.' . _ID_TYPE => 1], - ['value.' . _IMPACT_INDEX => 1], - [\_CREATED_TS => 1], - ]; - if (isset($spec['ensureIndexes'])) { - $indexes = array_merge($indexes, $spec['ensureIndexes']); - } - if ($reindex) { - if (!in_array($collection->getNamespace(), $reindexedCollections)) { - $collection->dropIndexes(); - $reindexedCollections[] = $collection->getNamespace(); - } - } - foreach ($indexes as $index) { - $collection->createIndex( - $index, - [ - 'background' => $background, - ] - ); - } + $indexes = [ + [_ID_KEY . '.' . _ID_RESOURCE => 1, _ID_KEY . '.' . _ID_CONTEXT => 1, _ID_KEY . '.' . _ID_TYPE => 1], + [_ID_KEY . '.' . _ID_TYPE => 1], + ['value.' . _IMPACT_INDEX => 1], + [\_CREATED_TS => 1], + ]; + if (isset($spec['ensureIndexes'])) { + $indexes = array_merge($indexes, $spec['ensureIndexes']); + } + + if ($reindex && !in_array($collection->getNamespace(), $reindexedCollections)) { + $collection->dropIndexes(); + $reindexedCollections[] = $collection->getNamespace(); + } + + foreach ($indexes as $index) { + $collection->createIndex( + $index, + [ + 'background' => $background, + ] + ); } } // Index table rows foreach ($config->getTableSpecifications($storeName) as $tableId => $spec) { $collection = $config->getCollectionForTable($storeName, $tableId); - if ($collection) { - $indexes = [ - [_ID_KEY . '.' . _ID_RESOURCE => 1, _ID_KEY . '.' . _ID_CONTEXT => 1, _ID_KEY . '.' . _ID_TYPE => 1], - [_ID_KEY . '.' . _ID_TYPE => 1], - ['value.' . _IMPACT_INDEX => 1], - [\_CREATED_TS => 1], - ]; - if (isset($spec['ensureIndexes'])) { - $indexes = array_merge($indexes, $spec['ensureIndexes']); - } - if ($reindex) { - if (!in_array($collection->getNamespace(), $reindexedCollections)) { - $collection->dropIndexes(); - $reindexedCollections[] = $collection->getNamespace(); - } - } - foreach ($indexes as $index) { - $collection->createIndex( - $index, - [ - 'background' => $background, - ] - ); - } + $indexes = [ + [_ID_KEY . '.' . _ID_RESOURCE => 1, _ID_KEY . '.' . _ID_CONTEXT => 1, _ID_KEY . '.' . _ID_TYPE => 1], + [_ID_KEY . '.' . _ID_TYPE => 1], + ['value.' . _IMPACT_INDEX => 1], + [\_CREATED_TS => 1], + ]; + if (isset($spec['ensureIndexes'])) { + $indexes = array_merge($indexes, $spec['ensureIndexes']); + } + + if ($reindex && !in_array($collection->getNamespace(), $reindexedCollections)) { + $collection->dropIndexes(); + $reindexedCollections[] = $collection->getNamespace(); + } + + foreach ($indexes as $index) { + $collection->createIndex( + $index, + [ + 'background' => $background, + ] + ); } } // index search documents - foreach ($config->getSearchDocumentSpecifications($storeName) as $searchId => $spec) { + foreach (array_keys($config->getSearchDocumentSpecifications($storeName)) as $searchId) { $collection = $config->getCollectionForSearchDocument($storeName, $searchId); - if ($collection) { - $indexes = [ - [_ID_KEY . '.' . _ID_RESOURCE => 1, _ID_KEY . '.' . _ID_CONTEXT => 1], - [_ID_KEY . '.' . _ID_TYPE => 1], - [_IMPACT_INDEX => 1], - [\_CREATED_TS => 1], - ]; + $indexes = [ + [_ID_KEY . '.' . _ID_RESOURCE => 1, _ID_KEY . '.' . _ID_CONTEXT => 1], + [_ID_KEY . '.' . _ID_TYPE => 1], + [_IMPACT_INDEX => 1], + [\_CREATED_TS => 1], + ]; - if ($reindex) { - if (!in_array($collection->getNamespace(), $reindexedCollections)) { - $collection->dropIndexes(); - $reindexedCollections[] = $collection->getNamespace(); - } - } - foreach ($indexes as $index) { - $collection->createIndex( - $index, - [ - 'background' => $background, - ] - ); - } + if ($reindex && !in_array($collection->getNamespace(), $reindexedCollections)) { + $collection->dropIndexes(); + $reindexedCollections[] = $collection->getNamespace(); + } + + foreach ($indexes as $index) { + $collection->createIndex( + $index, + [ + 'background' => $background, + ] + ); } } } @@ -157,10 +152,8 @@ public function ensureIndexes($reindex = false, $storeName = null, $background = /** * returns mongo tripod config instance, this method aids helps with * testing. - * - * @return \Tripod\Mongo\Config */ - protected function getConfig() + protected function getConfig(): IConfigInstance { return Config::getInstance(); } diff --git a/src/mongo/util/TriplesUtil.php b/src/mongo/util/TriplesUtil.php index e88190d9..4e5c98f7 100644 --- a/src/mongo/util/TriplesUtil.php +++ b/src/mongo/util/TriplesUtil.php @@ -1,5 +1,7 @@ getDefaultContextAlias() : $this->labeller->uri_to_alias($context); - if (array_key_exists($podName, $this->collections)) { - $collection = $this->collections[$podName]; - } else { - $collection = Config::getInstance()->getCollectionForCBD($storeName, $podName); - } + $context = $context !== null ? $this->labeller->uri_to_alias($context) : Config::getInstance()->getDefaultContextAlias(); + $collection = Config::getInstance()->getCollectionForCBD($storeName, $podName); $graph = new MongoGraph(); foreach ($triples as $triple) { @@ -57,11 +46,9 @@ public function loadTriplesAbout($subject, array $triples, $storeName, $podName, $graph->add_literal_triple($subject, $predicate, $object); } } - if ($allowableTypes != null && is_array($allowableTypes)) { + + if ($allowableTypes != null) { $types = $graph->get_resource_triple_values($subject, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'); - if ($types == null || empty($types)) { - return; - } foreach ($types as $type) { if (in_array($type, $allowableTypes)) { @@ -73,19 +60,15 @@ public function loadTriplesAbout($subject, array $triples, $storeName, $podName, return; } + $this->saveCBD($subject, $graph, $collection, $context); } /** * Add $triples about a given $subject to Mongo. Only $triples with subject matching $subject will be added, others will be ignored. * Make them quads with a $context. - * - * @param string $subject - * @param string|null $context - * - * @return array */ - public function bsonizeTriplesAbout($subject, array $triples, $context = null) + public function bsonizeTriplesAbout(string $subject, array $triples, ?string $context = null): ?array { $context = ($context == null) ? Config::getInstance()->getDefaultContextAlias() : $this->labeller->uri_to_alias($context); $graph = new MongoGraph(); @@ -107,10 +90,7 @@ public function bsonizeTriplesAbout($subject, array $triples, $context = null) return $graph->to_tripod_array($subject, $context); } - /** - * @return array - */ - public function extractMissingPredicateNs(array $triples) + public function extractMissingPredicateNs(array $triples): array { $missingNs = []; $graph = new MongoGraph(); @@ -130,10 +110,7 @@ public function extractMissingPredicateNs(array $triples) return array_unique($missingNs); } - /** - * @return array - */ - public function extractMissingObjectNs(array $triples) + public function extractMissingObjectNs(array $triples): array { $missingNs = []; $graph = new MongoGraph(); @@ -155,12 +132,7 @@ public function extractMissingObjectNs(array $triples) return array_unique($missingNs); } - /** - * @param string $ns - * - * @return string - */ - public function suggestPrefix($ns) + public function suggestPrefix(string $ns): string { $parts = preg_split('/[\/#]/', $ns); for ($i = count($parts) - 1; $i >= 0; $i--) { @@ -172,13 +144,7 @@ public function suggestPrefix($ns) return 'unknown' . uniqid(); } - /** - * @param string $subject - * @param string $context - * - * @return array - */ - public function getTArrayAbout($subject, array $triples, $context) + public function getTArrayAbout(string $subject, array $triples, ?string $context): ?array { $graph = new MongoGraph(); foreach ($triples as $triple) { @@ -200,16 +166,13 @@ public function getTArrayAbout($subject, array $triples, $context) } /** - * @param string $cbdSubject - * @param string $context - * * @throws \Exception */ - protected function saveCBD($cbdSubject, MongoGraph $cbdGraph, Collection $collection, $context) + protected function saveCBD(string $cbdSubject, MongoGraph $cbdGraph, Collection $collection, ?string $context): void { $cbdSubject = $this->labeller->uri_to_alias($cbdSubject); if ($cbdGraph == null || $cbdGraph->is_empty()) { - throw new \Exception("graph for {$cbdSubject} was null"); + throw new \Exception(sprintf('graph for %s was null', $cbdSubject)); } try { @@ -224,38 +187,25 @@ protected function saveCBD($cbdSubject, MongoGraph $cbdGraph, Collection $collec $existingGraph->add_tripod_array($collection->findOne($criteria)); $existingGraph->add_graph($cbdGraph); - try { - $collection->updateOne($criteria, ['$set' => $existingGraph->to_tripod_array($cbdSubject, $context)], ['w' => 1]); - } catch (\Exception $e2) { - throw new \Exception($e2->getMessage()); // todo: would be good to have typed exception - } + $collection->updateOne($criteria, ['$set' => $existingGraph->to_tripod_array($cbdSubject, $context)], ['w' => 1]); } else { // retry echo 'CursorException on update: ' . $e->getMessage() . ", retrying\n"; - try { - $collection->insertOne($cbdGraph->to_tripod_array($cbdSubject, $context), ['w' => 1]); - } catch (\Exception $e2) { - throw new \Exception($e2->getMessage()); // todo: would be good to have typed exception - } + $collection->insertOne($cbdGraph->to_tripod_array($cbdSubject, $context), ['w' => 1]); } } } - /** - * @param string $object - * - * @return mixed - */ - private function isUri($object) + private function isUri(string $object): bool { - return filter_var($object, FILTER_VALIDATE_URL); + return filter_var($object, FILTER_VALIDATE_URL) !== false; } /** - * @return string + * @param string[] $parts */ - private function extract_object(array $parts) + private function extract_object(array $parts): string { if (!$this->is_object_literal($parts[2])) { return trim($parts[2], '><'); @@ -271,7 +221,7 @@ private function extract_object(array $parts) $json_string = '{"string":"' . str_replace('\u', '\u', $str) . '"}'; $json = json_decode($json_string, true); if (!empty($json)) { - $str = $json['string']; + return $json['string']; } return $str; @@ -279,15 +229,9 @@ private function extract_object(array $parts) /** * @param string $input - * - * @return bool */ - private function is_object_literal($input) + private function is_object_literal($input): bool { - if ($input[0] == '"') { - return true; - } - - return false; + return $input[0] == '"'; } } diff --git a/test/bootstrap.php b/test/bootstrap.php index 03b85474..14962e0f 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -6,8 +6,8 @@ use Tripod\Mongo\Jobs\JobBase; require_once __DIR__ . '/../vendor/autoload.php'; - require_once __DIR__ . '/../src/tripod.inc.php'; +require_once __DIR__ . '/stubs.php'; // Mongo Config For Main DB define('MONGO_MAIN_DB', 'acorn'); diff --git a/test/performance/mongo/LargeGraphTest.php b/test/performance/mongo/LargeGraphTest.php index f68bbb50..42d8a016 100644 --- a/test/performance/mongo/LargeGraphTest.php +++ b/test/performance/mongo/LargeGraphTest.php @@ -1,5 +1,7 @@ loadLargeGraphData(); } - public function testUpdateSingleTripleOfLargeGraph() + public function testUpdateSingleTripleOfLargeGraph(): void { $uri = 'http://largegraph/1'; $testStartTime = microtime(); + $this->startProfiler(); + $graph = new ExtendedGraph(); $graph->add_literal_triple($uri, 'http://rdfs.org/sioc/spec/name', 'new name'); + $this->tripod->saveChanges(new ExtendedGraph(), $graph); $testEndTime = microtime(); @@ -48,14 +53,17 @@ public function testUpdateSingleTripleOfLargeGraph() ); } - public function testDescribeOfLargeGraph() + public function testDescribeOfLargeGraph(): void { $uri = 'http://largegraph/1'; $testStartTime = microtime(); + $this->startProfiler(); + $graph = new ExtendedGraph(); $graph->add_literal_triple($uri, 'http://rdfs.org/sioc/spec/name', 'new name'); + $this->tripod->describeResource($uri); $testEndTime = microtime(); @@ -75,7 +83,7 @@ protected function loadLargeGraphData() } } - protected function getConfigLocation() + protected function getConfigLocation(): string { return __DIR__ . '/../../unit/mongo/data/config.json'; } diff --git a/test/performance/mongo/MongoTripodConfigTest.php b/test/performance/mongo/MongoTripodConfigTest.php index 4a353613..37f14518 100644 --- a/test/performance/mongo/MongoTripodConfigTest.php +++ b/test/performance/mongo/MongoTripodConfigTest.php @@ -1,5 +1,7 @@ profiler = $this->getProfiler(); + $this->profiler->start(); + } + + /** + * @after + */ + protected function stopProfiler(): void + { + if ($this->profiler instanceof Profiler) { + $this->profiler->stop(); + $this->profiler = null; + } + } + + private function getProfiler(): Profiler + { + $profilerDir = __DIR__ . '/../../../profiler'; + if (!is_dir($profilerDir)) { + mkdir($profilerDir, 0777, true); + } + + return new Profiler([ + 'save.handler' => Profiler::SAVER_FILE, + 'save.handler.file' => [ + 'filename' => $profilerDir . '/xhgui.data.jsonl', + ], + 'profiler.replace_url' => fn (): string => $this->getName(true) . ($this->hasFailed() ? ' FAIL' : ''), + ]); + } } diff --git a/test/stubs.php b/test/stubs.php new file mode 100644 index 00000000..768a0911 --- /dev/null +++ b/test/stubs.php @@ -0,0 +1,7 @@ +add_literal_triple('http://some/subject/1', 'http://some/predicate', $value); @@ -29,14 +31,12 @@ public function testAddValidValueToLiteralResultsInTriple($value) $this->assertTrue($hasPropertyResult, 'The triple should have been added for this value'); } - public function addValidValueToLiteralResultsInTriple_Provider() + public function addValidValueToLiteralResultsInTriple_Provider(): iterable { - return [ - ['String'], - [1], - [1.2], - [true], - ]; + yield ['String']; + yield [1]; + yield [1.2]; + yield [true]; } /** @@ -44,7 +44,7 @@ public function addValidValueToLiteralResultsInTriple_Provider() * * @param mixed $value */ - public function testAddInvalidValueToLiteralResultsInNoTriple($value) + public function testAddInvalidValueToLiteralResultsInNoTriple($value): void { $graph = new ExtendedGraph(); $addResult = $graph->add_literal_triple('http://some/subject/1', 'http://some/predicate', $value); @@ -54,13 +54,11 @@ public function testAddInvalidValueToLiteralResultsInNoTriple($value) $this->assertFalse($hasPropertyResult, 'The triple should not have been added for this value'); } - public function addInvalidValueToLiteralResultsInNoTriple_Provider() + public function addInvalidValueToLiteralResultsInNoTriple_Provider(): iterable { - return [ - [null], - [new stdClass()], - [function (): void {}], - ]; + yield [null]; + yield [new stdClass()]; + yield [function (): void {}]; } /** @@ -68,26 +66,24 @@ public function addInvalidValueToLiteralResultsInNoTriple_Provider() * * @param mixed $value */ - public function testAddInvalidSubjectToLiteralThrowsException($value) + public function testAddInvalidSubjectToLiteralThrowsException($value): void { - $this->expectException(Tripod\Exceptions\Exception::class); + $this->expectExceptionMessageMatches('/^(The subject is invalid|Argument 1.+must be of the type string.+)$/'); $graph = new ExtendedGraph(); $graph->add_resource_triple($value, 'http://some/predicate', 'http://someplace.com'); } - public function addInvalidSubjectToLiteralResultsInNoTriple_Provider() + public function addInvalidSubjectToLiteralResultsInNoTriple_Provider(): iterable { - return [ - [''], - [1], - [1.2], - [true], - [[]], - [null], - [new stdClass()], - [function (): void {}], - ]; + yield ['']; + yield [1]; + yield [1.2]; + yield [true]; + yield [[]]; + yield [null]; + yield [new stdClass()]; + yield [function (): void {}]; } /** @@ -95,29 +91,27 @@ public function addInvalidSubjectToLiteralResultsInNoTriple_Provider() * * @param mixed $value */ - public function testAddInvalidPredicateToLiteralThrowsException($value) + public function testAddInvalidPredicateToLiteralThrowsException($value): void { - $this->expectException(Tripod\Exceptions\Exception::class); + $this->expectExceptionMessageMatches('/^(The predicate is invalid|Argument 2.+must be of the type string.+)$/'); $graph = new ExtendedGraph(); $graph->add_resource_triple('http://some/subject/1', $value, 'http://someplace.com'); } - public function addInvalidPredicateToLiteralResultsInNoTriple_Provider() + public function addInvalidPredicateToLiteralResultsInNoTriple_Provider(): iterable { - return [ - [''], - [1], - [1.2], - [true], - [[]], - [null], - [new stdClass()], - [function (): void {}], - ]; + yield ['']; + yield [1]; + yield [1.2]; + yield [true]; + yield [[]]; + yield [null]; + yield [new stdClass()]; + yield [function (): void {}]; } - public function testAddValidValueToResourceResultsInTriple() + public function testAddValidValueToResourceResultsInTriple(): void { $value = 'A String'; $graph = new ExtendedGraph(); @@ -133,28 +127,33 @@ public function testAddValidValueToResourceResultsInTriple() * * @param mixed $value */ - public function testAddInvalidValueToResourceResultsInNoTriple($value) + public function testAddInvalidValueToResourceResultsInNoTriple($value): void { $graph = new ExtendedGraph(); - $addResult = $graph->add_resource_triple('http://some/subject/1', 'http://some/predicate', $value); + try { + $addResult = $graph->add_resource_triple('http://some/subject/1', 'http://some/predicate', $value); + } catch (TypeError $typeError) { + $addResult = false; + } + $this->assertFalse($addResult, 'The triple should not have been added for this value'); $hasPropertyResult = $graph->subject_has_property('http://some/subject/1', 'http://some/predicate'); $this->assertFalse($hasPropertyResult, 'The triple should not have been added for this value'); } - public function addInvalidValueToResourceResultsInNoTriple_Provider() + public function addInvalidValueToResourceResultsInNoTriple_Provider(): iterable { - return [ - [1], - [1.2], - [true], - [[]], - [null], - [new stdClass()], - [function (): void {}], - ]; + yield ['']; + yield ['0']; + yield [1]; + yield [1.2]; + yield [true]; + yield [[]]; + yield [null]; + yield [new stdClass()]; + yield [function (): void {}]; } /** @@ -162,26 +161,24 @@ public function addInvalidValueToResourceResultsInNoTriple_Provider() * * @param mixed $value */ - public function testAddInvalidSubjectToResourceThrowsException($value) + public function testAddInvalidSubjectToResourceThrowsException($value): void { - $this->expectException(Tripod\Exceptions\Exception::class); + $this->expectExceptionMessageMatches('/^(The subject is invalid|Argument 1.+must be of the type string.+)$/'); $graph = new ExtendedGraph(); $graph->add_resource_triple($value, 'http://some/predicate', 'http://someplace.com'); } - public function addInvalidSubjectToResourceResultsInNoTriple_Provider() + public function addInvalidSubjectToResourceResultsInNoTriple_Provider(): iterable { - return [ - [''], - [1], - [1.2], - [true], - [[]], - [null], - [new stdClass()], - [function (): void {}], - ]; + yield ['']; + yield [1]; + yield [1.2]; + yield [true]; + yield [[]]; + yield [null]; + yield [new stdClass()]; + yield [function (): void {}]; } /** @@ -189,29 +186,27 @@ public function addInvalidSubjectToResourceResultsInNoTriple_Provider() * * @param mixed $value */ - public function testAddInvalidPredicateToResourceThrowsException($value) + public function testAddInvalidPredicateToResourceThrowsException($value): void { - $this->expectException(Tripod\Exceptions\Exception::class); + $this->expectExceptionMessageMatches('/^(The predicate is invalid|Argument 2.+must be of the type string.+)$/'); $graph = new ExtendedGraph(); $graph->add_resource_triple('http://some/subject/1', $value, 'http://someplace.com'); } - public function addInvalidPredicateToResourceResultsInNoTriple_Provider() + public function addInvalidPredicateToResourceResultsInNoTriple_Provider(): iterable { - return [ - [''], - [1], - [1.2], - [true], - [[]], - [null], - [new stdClass()], - [function (): void {}], - ]; + yield ['']; + yield [1]; + yield [1.2]; + yield [true]; + yield [[]]; + yield [null]; + yield [new stdClass()]; + yield [function (): void {}]; } - public function testRemoveProperties() + public function testRemoveProperties(): void { $graph = new ExtendedGraph(); @@ -226,69 +221,69 @@ public function testRemoveProperties() $this->assertFalse($graph->subject_has_property('http://some/subject/3', 'http://some/predicate/to/remove'), 'should have removed triple about subject 3'); } - public function testGetFirstResource() + public function testGetFirstResource(): void { $graph = new ExtendedGraph(); $graph->add_literal_triple('http://some/subject/1', 'http://some/predicate', 'value 1'); $graph->add_resource_triple('http://some/subject/1', 'http://some/predicate', 'http://value/2'); - $this->assertEquals('http://value/2', $graph->get_first_resource('http://some/subject/1', 'http://some/predicate'), 'should have returned first resource'); + $this->assertSame('http://value/2', $graph->get_first_resource('http://some/subject/1', 'http://some/predicate'), 'should have returned first resource'); $this->assertEquals(null, $graph->get_first_resource('http://some/subject/2', 'http://other/predicate'), 'should have returned default value'); - $this->assertEquals('my default', $graph->get_first_resource('http://some/subject/3', 'http://other/predicate', 'my default'), 'should have returned default value'); + $this->assertSame('my default', $graph->get_first_resource('http://some/subject/3', 'http://other/predicate', 'my default'), 'should have returned default value'); } - public function testRemoveResourceTriple() + public function testRemoveResourceTriple(): void { $graph = new ExtendedGraph(); // Add some triples $graph->add_resource_triple('http://some/subject/1', 'http://some/predicate', 'http://value/1'); $graph->add_resource_triple('http://some/subject/2', 'http://some/predicate', 'http://value/2'); - $this->assertEquals(2, $graph->get_triple_count(), 'should have 2 triples'); + $this->assertSame(2, $graph->get_triple_count(), 'should have 2 triples'); // Try to remove triples that don't exist $graph->remove_resource_triple('http://some/subject/3', 'http://some/predicate', 'http://value/3'); $graph->remove_literal_triple('http://some/subject/3', 'http://some/predicate', 'value 3'); - $this->assertEquals(2, $graph->get_triple_count(), 'should have 2 triples'); + $this->assertSame(2, $graph->get_triple_count(), 'should have 2 triples'); // Remove a triple that does exist $graph->remove_resource_triple('http://some/subject/1', 'http://some/predicate', 'http://value/1'); - $this->assertEquals(1, $graph->get_triple_count(), 'should have 1 triple'); + $this->assertSame(1, $graph->get_triple_count(), 'should have 1 triple'); // Remove the last triple $graph->remove_resource_triple('http://some/subject/2', 'http://some/predicate', 'http://value/2'); - $this->assertEquals(0, $graph->get_triple_count(), 'should have 0 triples'); + $this->assertSame(0, $graph->get_triple_count(), 'should have 0 triples'); $this->assertTrue($graph->is_empty(), 'should be empty'); - $this->assertEquals([], $graph->get_index(), 'should have empty index'); + $this->assertSame([], $graph->get_index(), 'should have empty index'); } - public function testRemoveLiteralTriple() + public function testRemoveLiteralTriple(): void { $graph = new ExtendedGraph(); // Add some triples $graph->add_literal_triple('http://some/subject/1', 'http://some/predicate', 'value 1'); $graph->add_literal_triple('http://some/subject/2', 'http://some/predicate', 'value 2'); - $this->assertEquals(2, $graph->get_triple_count(), 'should have 2 triples'); + $this->assertSame(2, $graph->get_triple_count(), 'should have 2 triples'); // Try to remove triples that don't exist $graph->remove_literal_triple('http://some/subject/3', 'http://some/predicate', 'value 3'); $graph->remove_resource_triple('http://some/subject/3', 'http://some/predicate', 'http://value/3'); - $this->assertEquals(2, $graph->get_triple_count(), 'should have 2 triples'); + $this->assertSame(2, $graph->get_triple_count(), 'should have 2 triples'); // Remove a triple that does exist $graph->remove_literal_triple('http://some/subject/1', 'http://some/predicate', 'value 1'); - $this->assertEquals(1, $graph->get_triple_count(), 'should have 1 triple'); + $this->assertSame(1, $graph->get_triple_count(), 'should have 1 triple'); // Remove the last triple $graph->remove_literal_triple('http://some/subject/2', 'http://some/predicate', 'value 2'); - $this->assertEquals(0, $graph->get_triple_count(), 'should have 0 triples'); + $this->assertSame(0, $graph->get_triple_count(), 'should have 0 triples'); $this->assertTrue($graph->is_empty(), 'should be empty'); - $this->assertEquals([], $graph->get_index(), 'should have empty index'); + $this->assertSame([], $graph->get_index(), 'should have empty index'); } - public function testGetResourceProperties() + public function testGetResourceProperties(): void { $graph = new ExtendedGraph(); @@ -301,7 +296,7 @@ public function testGetResourceProperties() $this->assertEquals($values, ['http://value/1', 'http://value/2', 'http://value/3'], 'should have returned 3 values'); } - public function testGetSubjectsWithPropertyValue() + public function testGetSubjectsWithPropertyValue(): void { $graph = new ExtendedGraph(); @@ -316,7 +311,7 @@ public function testGetSubjectsWithPropertyValue() $this->assertEquals($subjects, ['http://some/subject/2', 'http://some/subject/2-with-literal'], 'should have returned correct subject'); } - public function testGetSequenceValues() + public function testGetSequenceValues(): void { $graph = new ExtendedGraph(); $graph->add_resource_triple('http://some/subject/1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_4', 'http://value/4'); @@ -326,10 +321,10 @@ public function testGetSequenceValues() $graph->add_resource_triple('http://some/subject/1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#_1', 'http://value/1'); $expectedArray = ['http://value/1', 'http://value/2', 'http://value/3', 'http://value/4', 'http://value/5']; - $this->assertEquals($expectedArray, $graph->get_sequence_values('http://some/subject/1')); + $this->assertSame($expectedArray, $graph->get_sequence_values('http://some/subject/1')); } - public function testAddResourceToSequence() + public function testAddResourceToSequence(): void { $testSubject = 'http://some/subject/s1'; $testObject1 = 'http://some/object/o1'; @@ -339,23 +334,23 @@ public function testAddResourceToSequence() $graph->add_resource_to_sequence($testSubject, $testObject1); $objects = $graph->get_sequence_values('http://some/subject/s1'); - $this->assertEquals([$testObject1], $objects); + $this->assertSame([$testObject1], $objects); $graph->add_resource_to_sequence($testSubject, $testObject2); $objects = $graph->get_sequence_values('http://some/subject/s1'); - $this->assertEquals([$testObject1, $testObject2], $objects); + $this->assertSame([$testObject1, $testObject2], $objects); $graph->add_resource_to_sequence('http://some/other/subject', 'http://some/other/object'); $objects = $graph->get_sequence_values('http://some/subject/s1'); - $this->assertEquals([$testObject1, $testObject2], $objects); + $this->assertSame([$testObject1, $testObject2], $objects); $objects = $graph->get_sequence_values('http://some/other/subject'); - $this->assertEquals(['http://some/other/object'], $objects); + $this->assertSame(['http://some/other/object'], $objects); } - public function testAddResourceToSequenceInPosition() + public function testAddResourceToSequenceInPosition(): void { $testSubject = 'http://some/subject/s1'; $testObject1 = 'http://some/object/o1'; @@ -368,30 +363,30 @@ public function testAddResourceToSequenceInPosition() $graph->add_resource_to_sequence_in_position($testSubject, $testObject1, 1); $objects = $graph->get_sequence_values('http://some/subject/s1'); - $this->assertEquals([$testObject1], $objects); + $this->assertSame([$testObject1], $objects); $graph->add_resource_to_sequence_in_position($testSubject, $testObject2, 1); $objects = $graph->get_sequence_values('http://some/subject/s1'); - $this->assertEquals([$testObject2, $testObject1], $objects); + $this->assertSame([$testObject2, $testObject1], $objects); $graph->add_resource_to_sequence_in_position($testSubject, $testObject3, 1); $objects = $graph->get_sequence_values('http://some/subject/s1'); - $this->assertEquals([$testObject3, $testObject2, $testObject1], $objects); + $this->assertSame([$testObject3, $testObject2, $testObject1], $objects); $graph->add_resource_to_sequence_in_position($testSubject, $testObject4, 3); $objects = $graph->get_sequence_values('http://some/subject/s1'); - $this->assertEquals([$testObject3, $testObject2, $testObject4, $testObject1], $objects); + $this->assertSame([$testObject3, $testObject2, $testObject4, $testObject1], $objects); $graph->add_resource_to_sequence_in_position($testSubject, $testObject5, count($objects) + 1); $objects = $graph->get_sequence_values('http://some/subject/s1'); - $this->assertEquals([$testObject3, $testObject2, $testObject4, $testObject1, $testObject5], $objects); + $this->assertSame([$testObject3, $testObject2, $testObject4, $testObject1, $testObject5], $objects); } - public function testAddToSequenceInPositionAgain() + public function testAddToSequenceInPositionAgain(): void { $graph = new ExtendedGraph(); $graph->add_resource_to_sequence('http://seq', 'http://item/1'); @@ -408,7 +403,7 @@ public function testAddToSequenceInPositionAgain() $this->assertTrue($graph->has_resource_triple('http://seq', ExtendedGraph::rdf . '_5', 'http://item/4')); } - public function testAddLiteralToSequence() + public function testAddLiteralToSequence(): void { $testSubject = 'http://some/subject/s1'; $testObject1 = 'foo1'; @@ -418,71 +413,75 @@ public function testAddLiteralToSequence() $graph->add_literal_to_sequence($testSubject, $testObject1); $objects = $graph->get_sequence_values('http://some/subject/s1'); - $this->assertEquals([$testObject1], $objects); + $this->assertSame([$testObject1], $objects); $graph->add_literal_to_sequence($testSubject, $testObject2); $objects = $graph->get_sequence_values('http://some/subject/s1'); - $this->assertEquals([$testObject1, $testObject2], $objects); + $this->assertSame([$testObject1, $testObject2], $objects); $graph->add_literal_to_sequence('http://some/other/subject', 'bar'); $objects = $graph->get_sequence_values('http://some/subject/s1'); - $this->assertEquals([$testObject1, $testObject2], $objects); + $this->assertSame([$testObject1, $testObject2], $objects); $objects = $graph->get_sequence_values('http://some/other/subject'); - $this->assertEquals(['bar'], $objects); + $this->assertSame(['bar'], $objects); } - public function testGetTripleCountWithNoParams() + public function testGetTripleCountWithNoParams(): void { $graph = new ExtendedGraph(); $graph->add_literal_triple('http://some/subject/1', 'http://some/predicate', 'some object'); $graph->add_literal_triple('http://some/subject/2', 'http://some/predicate', 'some object'); $graph->add_literal_triple('http://some/subject/3', 'http://some/predicate', 'some object'); + $expected = 3; $actual = $graph->get_triple_count(); - $this->assertEquals($expected, $actual); + $this->assertSame($expected, $actual); } - public function testGetTripleCountWithSubject() + public function testGetTripleCountWithSubject(): void { $graph = new ExtendedGraph(); $graph->add_literal_triple('http://some/subject/1', 'http://some/predicate', 'some object'); $graph->add_literal_triple('http://some/subject/2', 'http://some/predicate', 'some object'); $graph->add_literal_triple('http://some/subject/3', 'http://some/predicate', 'some object'); + $expected = 1; $actual = $graph->get_triple_count('http://some/subject/1'); - $this->assertEquals($expected, $actual); + $this->assertSame($expected, $actual); } - public function testGetTripleCountWithPredicate() + public function testGetTripleCountWithPredicate(): void { $graph = new ExtendedGraph(); $graph->add_literal_triple('http://some/subject/1', 'http://some/predicate', 'some object'); $graph->add_literal_triple('http://some/subject/2', 'http://some/predicate', 'some object'); $graph->add_literal_triple('http://some/subject/3', 'http://some/predicate', 'some object'); + $expected = 3; - $actual = $graph->get_triple_count(false, 'http://some/predicate'); - $this->assertEquals($expected, $actual); + $actual = $graph->get_triple_count(null, 'http://some/predicate'); + $this->assertSame($expected, $actual); } - public function testGetTripleCountWithObject() + public function testGetTripleCountWithObject(): void { $graph = new ExtendedGraph(); $graph->add_literal_triple('http://some/subject/1', 'http://some/predicate', 'some object'); $graph->add_literal_triple('http://some/subject/2', 'http://some/predicate', 'some object'); $graph->add_literal_triple('http://some/subject/3', 'http://some/predicate', 'some object'); + $expected = 3; - $actual = $graph->get_triple_count(false, false, 'some object'); - $this->assertEquals($expected, $actual); + $actual = $graph->get_triple_count(null, null, 'some object'); + $this->assertSame($expected, $actual); } - public function testGetTripleCountWithSubjectandPredicate() + public function testGetTripleCountWithSubjectandPredicate(): void { $graph = new ExtendedGraph(); @@ -490,12 +489,13 @@ public function testGetTripleCountWithSubjectandPredicate() $graph->add_literal_triple('http://some/subject/1', 'http://some/predicate', 'another object'); $graph->add_literal_triple('http://some/subject/2', 'http://some/predicate', 'some object'); $graph->add_literal_triple('http://some/subject/3', 'http://some/predicate', 'some object'); + $expected = 2; $actual = $graph->get_triple_count('http://some/subject/1', 'http://some/predicate'); - $this->assertEquals($expected, $actual); + $this->assertSame($expected, $actual); } - public function testGetTripleCountWithSubjectPredicateAndObject() + public function testGetTripleCountWithSubjectPredicateAndObject(): void { $graph = new ExtendedGraph(); @@ -503,34 +503,36 @@ public function testGetTripleCountWithSubjectPredicateAndObject() $graph->add_literal_triple('http://some/subject/1', 'http://some/predicate', 'another object'); $graph->add_literal_triple('http://some/subject/2', 'http://some/predicate', 'some object'); $graph->add_literal_triple('http://some/subject/3', 'http://some/predicate', 'some object'); + $expected = 1; $actual = $graph->get_triple_count('http://some/subject/1', 'http://some/predicate', 'some object'); - $this->assertEquals($expected, $actual); + $this->assertSame($expected, $actual); } - public function testGetTripleCountWithEmptyGraph() + public function testGetTripleCountWithEmptyGraph(): void { $graph = new ExtendedGraph(); $expected = 0; $actual = $graph->get_triple_count(); - $this->assertEquals($expected, $actual); + $this->assertSame($expected, $actual); } - public function testGetTripleCountWithNonExistentSubject() + public function testGetTripleCountWithNonExistentSubject(): void { $graph = new ExtendedGraph(); $expected = 0; $actual = $graph->get_triple_count('http://example.com/subject'); - $this->assertEquals($expected, $actual); + $this->assertSame($expected, $actual); } - public function testReplaceUris() + public function testReplaceUris(): void { $graph = new ExtendedGraph(); $graph->add_literal_triple('http://some/subject/1', 'http://some/predicate', 'some object'); $graph->add_literal_triple('http://some/subject/4', 'http://some/predicate', 'http://some/subject/1'); $graph->add_resource_triple('http://some/subject/2', 'http://some/predicate', 'http://some/subject/1'); $graph->replace_uris('http://some/subject/1', 'http://some/subject/3'); + $index = $graph->get_index(); $this->assertFalse($graph->has_triples_about('http://some/subject/1'), 'resource with old uri still exists'); $this->assertTrue($graph->has_triples_about('http://some/subject/3'), "resource with new uri doesn't exists"); @@ -539,21 +541,22 @@ public function testReplaceUris() $graph->replace_uris('http://some/predicate', 'http://some/predicate2'); $index = $graph->get_index(); - $this->assertTrue(isset($index['http://some/subject/2']['http://some/predicate2']), 'predicate should be replaced'); - $this->assertFalse(isset($index['http://some/subject/2']['http://some/predicate']), 'predicate should be replaced and old one not be in graph'); + $this->assertArrayHasKey('http://some/predicate2', $index['http://some/subject/2'], 'predicate should be replaced'); + $this->assertArrayNotHasKey('http://some/predicate', $index['http://some/subject/2'], 'predicate should be replaced and old one not be in graph'); } - public function testReplaceResourceTriples() + public function testReplaceResourceTriples(): void { $graph = new ExtendedGraph(); $graph->add_literal_triple('http://some/subject/1', 'http://some/predicate', 'some object'); $graph->add_resource_triple('http://some/subject/2', 'http://some/predicate', 'http://some/subject/1'); $graph->replace_resource_triples('http://some/subject/2', 'http://some/predicate', 'http://some/subject/3'); + $index = $graph->get_index(); $this->assertEquals('http://some/subject/3', $index['http://some/subject/2']['http://some/predicate'][0]['value'], 'http://some/subject/3'); } - public function testReplaceLiteralTriple() + public function testReplaceLiteralTriple(): void { $graph = new ExtendedGraph(); $graph->add_literal_triple('http://some/subject/s1', 'http://some/predicate', 'some object'); @@ -562,14 +565,14 @@ public function testReplaceLiteralTriple() $this->assertEquals('replacement object', $index['http://some/subject/s1']['http://some/predicate'][0]['value'], "should be 'replacement object'"); } - public function testReplaceLiteralTripleReturnsFalseIfNoReplacementMade() + public function testReplaceLiteralTripleReturnsFalseIfNoReplacementMade(): void { $graph = new ExtendedGraph(); $graph->add_literal_triple('http://some/subject/s1', 'http://some/predicate', 'some object'); $this->assertFalse($graph->replace_literal_triple('http://some/othersubject/s1', 'http://some/predicate', 'some object', 'replacement object'), 'Should return FALSE'); } - public function testGetResources() + public function testGetResources(): void { $graph = new ExtendedGraph(); @@ -585,11 +588,11 @@ public function testGetResources() sort($expected); sort($actual); - $this->assertEquals(count($expected), count($actual), 'should get same number of resource uris'); + $this->assertCount(count($expected), $actual, 'should get same number of resource uris'); $this->assertEquals($expected, $actual, 'should get expected array containing all the resource uris'); } - public function testGetLabelForUri() + public function testGetLabelForUri(): void { $graph = new ExtendedGraph(); @@ -602,10 +605,10 @@ public function testGetLabelForUri() ExtendedGraph::initProperties(['labelProperties' => ['http://www.w3.org/2000/01/rdf-schema#label']]); - $this->assertEquals($graph->get_label_for_uri($s1), $label, 'get_label_for_uri(uri) should return the value of a label property'); + $this->assertSame($graph->get_label_for_uri($s1), $label, 'get_label_for_uri(uri) should return the value of a label property'); } - public function testGetLabelForUriLabelPropsNotInitialised() + public function testGetLabelForUriLabelPropsNotInitialised(): void { ExtendedGraph::initProperties(['labelProperties' => null]); $this->expectException(Exception::class); @@ -622,7 +625,7 @@ public function testGetLabelForUriLabelPropsNotInitialised() $graph->get_label_for_uri($s1); } - public function testGetLabelForUriReturnsEmptyStringIfSubjectNotFound() + public function testGetLabelForUriReturnsEmptyStringIfSubjectNotFound(): void { $graph = new ExtendedGraph(); @@ -635,10 +638,10 @@ public function testGetLabelForUriReturnsEmptyStringIfSubjectNotFound() ExtendedGraph::initProperties(['labelProperties' => ['http://www.w3.org/2000/01/rdf-schema#label']]); - $this->assertEquals($graph->get_label_for_uri('http://example.com/1'), ''); + $this->assertSame('', $graph->get_label_for_uri('http://example.com/1')); } - public function testGetLabelForUriReturnsEmptyStringLabelNotFound() + public function testGetLabelForUriReturnsEmptyStringLabelNotFound(): void { $graph = new ExtendedGraph(); @@ -651,10 +654,10 @@ public function testGetLabelForUriReturnsEmptyStringLabelNotFound() ExtendedGraph::initProperties(['labelProperties' => ['http://www.w3.org/2000/01/rdf-schema#label2']]); - $this->assertEquals($graph->get_label_for_uri($s1), ''); + $this->assertSame('', $graph->get_label_for_uri($s1)); } - public function testIsEqualToReturnsTrueForIdenticalGraphs() + public function testIsEqualToReturnsTrueForIdenticalGraphs(): void { $s = 'http://example.com/people/bloggs-joe'; $rdfType = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; @@ -669,7 +672,7 @@ public function testIsEqualToReturnsTrueForIdenticalGraphs() $this->assertTrue($graph2->is_equal_to($graph1), 'graph2 should equal graph1'); } - public function testIsEqualToReturnsFalseForDifferingGraphs() + public function testIsEqualToReturnsFalseForDifferingGraphs(): void { $rdfType = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; @@ -708,7 +711,7 @@ public function testIsEqualToReturnsFalseForDifferingGraphs() $this->assertFalse($graph5->is_equal_to($graph2), 'graph5 should not equal graph2'); } - public function testIsEqualToIgnoresNamespaceDifferences() + public function testIsEqualToIgnoresNamespaceDifferences(): void { $s = 'http://example.com/people/bloggs-joe'; $rdfType = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; @@ -731,7 +734,7 @@ public function testIsEqualToIgnoresNamespaceDifferences() $this->assertTrue($graph3->is_equal_to($graph2), 'graph3 should equal graph2'); } - public function testRemoveResourceFromSequence() + public function testRemoveResourceFromSequence(): void { $graph = new ExtendedGraph(); @@ -747,16 +750,15 @@ public function testRemoveResourceFromSequence() $graph->remove_resource_from_sequence($s, $sub2); $sequenceValues = $graph->get_sequence_values($s); - $this->assertEquals([$sub1, $sub3], $sequenceValues, 'There should be two sequence values, in the correct order'); + $this->assertSame([$sub1, $sub3], $sequenceValues, 'There should be two sequence values, in the correct order'); $this->assertTrue($graph->has_resource_triple($s, ExtendedGraph::rdf . '_1', $sub1)); $this->assertTrue($graph->has_resource_triple($s, ExtendedGraph::rdf . '_2', $sub3)); } - public function testFromGraph() + public function testFromGraph(): void { $itemUri = 'http://foo/item'; $mainResourceUri = 'http://foo/mainResource'; - $partOfResourceUri = 'http://foo/partOfResource'; $itemGraph = new ExtendedGraph(); @@ -769,7 +771,7 @@ public function testFromGraph() $this->assertTrue($graph->is_equal_to($itemGraph)); } - public function testRemoveSubjectsOfType() + public function testRemoveSubjectsOfType(): void { $graph = new ExtendedGraph(); $graph->add_resource_triple('http://test/1', ExtendedGraph::rdf . 'type', self::ONT_resource . 'Item'); @@ -779,11 +781,11 @@ public function testRemoveSubjectsOfType() $graph->remove_subjects_of_type(self::ONT_resource . 'Item'); $subjects = $graph->get_subjects(); - $this->assertEquals(1, count($subjects)); + $this->assertCount(1, $subjects); $this->assertEquals('http://test/3', $subjects[0]); } - public function testReplaceLiteralTriples() + public function testReplaceLiteralTriples(): void { $graph = new ExtendedGraph(); $graph->add_literal_triple('http://test/1', 'http://www.w3.org/2000/01/rdf-schema#label', 'value1'); @@ -795,7 +797,7 @@ public function testReplaceLiteralTriples() $this->assertFalse($graph->has_literal_triple('http://test/1', 'http://www.w3.org/2000/01/rdf-schema#label', 'value2')); } - public function testFromJson() + public function testFromJson(): void { $graph = new ExtendedGraph(); $graph->from_json('{ @@ -816,12 +818,12 @@ public function testFromJson() } }'); - $this->assertEquals(3, count($graph->get_subjects())); - $this->assertEquals(['http://subject/1', 'http://subject/2', 'http://subject/3'], $graph->get_subjects()); - $this->assertEquals(['http://value/1', 'http://value/2', 'http://value/3'], $graph->get_resource_properties('http://predicate')); + $this->assertCount(3, $graph->get_subjects()); + $this->assertSame(['http://subject/1', 'http://subject/2', 'http://subject/3'], $graph->get_subjects()); + $this->assertSame(['http://value/1', 'http://value/2', 'http://value/3'], $graph->get_resource_properties('http://predicate')); } - public function testFromInvalidJson() + public function testFromInvalidJson(): void { $graph = new ExtendedGraph(); $index = $graph->get_index(); @@ -832,7 +834,7 @@ public function testFromInvalidJson() $this->assertEquals($index, $graph->get_index()); } - public function testAddJson() + public function testAddJson(): void { $graph = new ExtendedGraph(); $graph->add_json('{ @@ -843,9 +845,9 @@ public function testAddJson() } }'); - $this->assertEquals(1, count($graph->get_subjects())); - $this->assertEquals(['http://subject/1'], $graph->get_subjects()); - $this->assertEquals(['http://value/1'], $graph->get_resource_properties('http://predicate')); + $this->assertCount(1, $graph->get_subjects()); + $this->assertSame(['http://subject/1'], $graph->get_subjects()); + $this->assertSame(['http://value/1'], $graph->get_resource_properties('http://predicate')); $graph->add_json('{ "http://subject/2": { @@ -855,12 +857,12 @@ public function testAddJson() } }'); - $this->assertEquals(2, count($graph->get_subjects())); - $this->assertEquals(['http://subject/1', 'http://subject/2'], $graph->get_subjects()); - $this->assertEquals(['http://value/1', 'http://value/2'], $graph->get_resource_properties('http://predicate')); + $this->assertCount(2, $graph->get_subjects()); + $this->assertSame(['http://subject/1', 'http://subject/2'], $graph->get_subjects()); + $this->assertSame(['http://value/1', 'http://value/2'], $graph->get_resource_properties('http://predicate')); } - public function testAddInvalidJson() + public function testAddInvalidJson(): void { $graph = new ExtendedGraph(); $graph->add_json('{ @@ -871,16 +873,16 @@ public function testAddInvalidJson() } }'); - $this->assertEquals(1, $graph->get_triple_count()); - $this->assertEquals(1, count($graph->get_subjects())); - $this->assertEquals(['http://subject/1'], $graph->get_subjects()); - $this->assertEquals(['http://value/1'], $graph->get_resource_properties('http://predicate')); + $this->assertSame(1, $graph->get_triple_count()); + $this->assertCount(1, $graph->get_subjects()); + $this->assertSame(['http://subject/1'], $graph->get_subjects()); + $this->assertSame(['http://value/1'], $graph->get_resource_properties('http://predicate')); $index = $graph->get_index(); $graph->add_json('not a valid json'); // Should not have changed $this->assertEquals($index, $graph->get_index()); - $this->assertEquals(1, $graph->get_triple_count()); + $this->assertSame(1, $graph->get_triple_count()); } } diff --git a/test/unit/TimerTest.php b/test/unit/TimerTest.php index 3944986c..04b299fb 100644 --- a/test/unit/TimerTest.php +++ b/test/unit/TimerTest.php @@ -1,5 +1,7 @@ expectException(Exception::class); @@ -19,7 +21,7 @@ public function testResultWhenStartTimeNotSet() $timer->result(); } - public function testResultWhenEndTimeNotSet() + public function testResultWhenEndTimeNotSet(): void { $timer = new Timer(); $timer->start(); @@ -28,19 +30,20 @@ public function testResultWhenEndTimeNotSet() $timer->result(); } - public function testResultGetTimeInMilliSeconds() + public function testResultGetTimeInMilliSeconds(): void { $timer = new Timer(); $timer->start(); sleep(1); // Let's pause for one seconds otherwise we will get 0 as a result. $timer->stop(); - $status = ($timer->result() >= 1000) ? true : false; + $status = $timer->result() >= 1000; $this->assertTrue($status); } + /** END: result() tests */ /** START: microResult() tests */ - public function testMicroResultWhenStartTimeNotSet() + public function testMicroResultWhenStartTimeNotSet(): void { $timer = new Timer(); $this->expectException(Exception::class); @@ -48,7 +51,7 @@ public function testMicroResultWhenStartTimeNotSet() $timer->result(); } - public function testMicroResultWhenEndTimeNotSet() + public function testMicroResultWhenEndTimeNotSet(): void { $timer = new Timer(); $timer->start(); @@ -57,14 +60,15 @@ public function testMicroResultWhenEndTimeNotSet() $timer->result(); } - public function testMicroResultGetTimeInMilliSeconds() + public function testMicroResultGetTimeInMilliSeconds(): void { $timer = new Timer(); $timer->start(); sleep(1); // Let's pause for one seconds otherwise we might get 0 as a result. $timer->stop(); - $status = ($timer->microResult() >= 1000000) ? true : false; + $status = $timer->microResult() >= 1000000; $this->assertTrue($status); } + // END: microResult() tests } diff --git a/test/unit/mongo/ApplyOperationTest.php b/test/unit/mongo/ApplyOperationTest.php index dbc43e59..e7624706 100644 --- a/test/unit/mongo/ApplyOperationTest.php +++ b/test/unit/mongo/ApplyOperationTest.php @@ -1,5 +1,7 @@ setArgs(); unset($this->args['tripodConfig']); @@ -29,7 +31,7 @@ public function testMandatoryArgTripodConfig() $this->performJob($job); } - public function testMandatoryArgSubject() + public function testMandatoryArgSubject(): void { $this->setArgs(); unset($this->args['subjects']); @@ -41,7 +43,7 @@ public function testMandatoryArgSubject() $this->performJob($job); } - public function testApplyViewOperation() + public function testApplyViewOperation(): void { $this->setArgs(); $applyOperation = $this->getMockBuilder(ApplyOperation::class) @@ -87,11 +89,11 @@ public function testApplyViewOperation() $applyOperation->expects($this->once()) ->method('createImpactedSubject') - ->will($this->returnValue($subject)); + ->willReturn($subject); $applyOperation->expects($this->exactly(3)) ->method('getStat') - ->will($this->returnValue($statMock)); + ->willReturn($statMock); $statMock->expects($this->once()) ->method('increment') @@ -105,12 +107,12 @@ public function testApplyViewOperation() $subject->expects($this->once()) ->method('getTripod') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $tripod->expects($this->once()) ->method('getComposite') ->with(OP_VIEWS) - ->will($this->returnValue($views)); + ->willReturn($views); $views->expects($this->once()) ->method('update') @@ -119,7 +121,7 @@ public function testApplyViewOperation() $this->performJob($applyOperation); } - public function testApplyViewOperationDecrementsJobGroupForBatchOperations() + public function testApplyViewOperationDecrementsJobGroupForBatchOperations(): void { $this->setArgs(); $applyOperation = $this->getMockBuilder(ApplyOperation::class) @@ -128,6 +130,7 @@ public function testApplyViewOperationDecrementsJobGroupForBatchOperations() $applyOperation->args = $this->args; $applyOperation->job = new Resque_Job('queue', ['id' => uniqid()]); + $jobTrackerId = new ObjectId(); $applyOperation->args[ApplyOperation::TRACKING_KEY] = $jobTrackerId->__toString(); @@ -139,7 +142,7 @@ public function testApplyViewOperationDecrementsJobGroupForBatchOperations() $jobGroup->expects($this->once()) ->method('incrementJobCount') ->with(-1) - ->will($this->returnValue(2)); + ->willReturn(2); $statMock = $this->getMockStat( $this->args['statsConfig']['config']['host'], @@ -179,16 +182,16 @@ public function testApplyViewOperationDecrementsJobGroupForBatchOperations() $applyOperation->expects($this->once()) ->method('createImpactedSubject') - ->will($this->returnValue($subject)); + ->willReturn($subject); $applyOperation->expects($this->exactly(3)) ->method('getStat') - ->will($this->returnValue($statMock)); + ->willReturn($statMock); $applyOperation->expects($this->once()) ->method('getJobGroup') ->with('tripod_php_testing', $jobTrackerId->__toString()) - ->will($this->returnValue($jobGroup)); + ->willReturn($jobGroup); $applyOperation->expects($this->never()) ->method('getTripod'); @@ -205,12 +208,12 @@ public function testApplyViewOperationDecrementsJobGroupForBatchOperations() $subject->expects($this->once()) ->method('getTripod') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $tripod->expects($this->once()) ->method('getComposite') ->with(OP_VIEWS) - ->will($this->returnValue($views)); + ->willReturn($views); $views->expects($this->once()) ->method('update') @@ -220,7 +223,7 @@ public function testApplyViewOperationDecrementsJobGroupForBatchOperations() $this->performJob($applyOperation); } - public function testApplyViewOperationCleanupIfAllGroupJobsComplete() + public function testApplyViewOperationCleanupIfAllGroupJobsComplete(): void { $this->setArgs(OP_VIEWS, ['v_foo_bar']); $applyOperation = $this->getMockBuilder(ApplyOperation::class) @@ -229,9 +232,10 @@ public function testApplyViewOperationCleanupIfAllGroupJobsComplete() $applyOperation->args = $this->args; $applyOperation->job = new Resque_Job('queue', ['id' => uniqid()]); + $jobTrackerId = new ObjectId(); $applyOperation->args[ApplyOperation::TRACKING_KEY] = $jobTrackerId->__toString(); - $timestamp = new UTCDateTime(hexdec(substr($jobTrackerId, 0, 8)) * 1000); + $timestamp = new UTCDateTime($jobTrackerId->getTimestamp() * 1000); $jobGroup = $this->getMockBuilder(JobGroup::class) ->onlyMethods(['incrementJobCount']) @@ -241,7 +245,7 @@ public function testApplyViewOperationCleanupIfAllGroupJobsComplete() $jobGroup->expects($this->once()) ->method('incrementJobCount') ->with(-1) - ->will($this->returnValue(0)); + ->willReturn(0); $statMock = $this->getMockStat( $this->args['statsConfig']['config']['host'], @@ -281,21 +285,21 @@ public function testApplyViewOperationCleanupIfAllGroupJobsComplete() $applyOperation->expects($this->once()) ->method('createImpactedSubject') - ->will($this->returnValue($subject)); + ->willReturn($subject); $applyOperation->expects($this->exactly(3)) ->method('getStat') - ->will($this->returnValue($statMock)); + ->willReturn($statMock); $applyOperation->expects($this->once()) ->method('getJobGroup') ->with('tripod_php_testing', $jobTrackerId->__toString()) - ->will($this->returnValue($jobGroup)); + ->willReturn($jobGroup); $applyOperation->expects($this->once()) ->method('getTripod') ->with('tripod_php_testing', 'CBD_testing') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $statMock->expects($this->once()) ->method('increment') @@ -309,11 +313,11 @@ public function testApplyViewOperationCleanupIfAllGroupJobsComplete() $subject->expects($this->once()) ->method('getTripod') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $tripod->expects($this->exactly(2)) ->method('getTripodViews') - ->will($this->returnValue($views)); + ->willReturn($views); $views->expects($this->once()) ->method('update') @@ -322,12 +326,12 @@ public function testApplyViewOperationCleanupIfAllGroupJobsComplete() $views->expects($this->once()) ->method('deleteViewsByViewId') ->with('v_foo_bar', $timestamp) - ->will($this->returnValue(3)); + ->willReturn(3); $this->performJob($applyOperation); } - public function testApplyTableOperation() + public function testApplyTableOperation(): void { $this->setArgs(); $applyOperation = $this->getMockBuilder(ApplyOperation::class) @@ -385,11 +389,11 @@ public function testApplyTableOperation() $applyOperation->expects($this->once()) ->method('createImpactedSubject') - ->will($this->returnValue($subject)); + ->willReturn($subject); $applyOperation->expects($this->exactly(3)) ->method('getStat') - ->will($this->returnValue($statMock)); + ->willReturn($statMock); $statMock->expects($this->once()) ->method('increment') @@ -403,12 +407,12 @@ public function testApplyTableOperation() $subject->expects($this->once()) ->method('getTripod') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $tripod->expects($this->once()) ->method('getComposite') ->with(OP_TABLES) - ->will($this->returnValue($tables)); + ->willReturn($tables); $tables->expects($this->once()) ->method('update') @@ -417,7 +421,7 @@ public function testApplyTableOperation() $this->performJob($applyOperation); } - public function testApplyTableOperationDecrementsJobGroupForBatchOperations() + public function testApplyTableOperationDecrementsJobGroupForBatchOperations(): void { $this->setArgs(OP_TABLES, ['t_resource']); $applyOperation = $this->getMockBuilder(ApplyOperation::class) @@ -445,7 +449,7 @@ public function testApplyTableOperationDecrementsJobGroupForBatchOperations() $jobGroup->expects($this->once()) ->method('incrementJobCount') ->with(-1) - ->will($this->returnValue(2)); + ->willReturn(2); $subject = $this->getMockBuilder(ImpactedSubject::class) ->onlyMethods(['getTripod']) @@ -478,16 +482,16 @@ public function testApplyTableOperationDecrementsJobGroupForBatchOperations() $applyOperation->expects($this->once()) ->method('createImpactedSubject') - ->will($this->returnValue($subject)); + ->willReturn($subject); $applyOperation->expects($this->exactly(3)) ->method('getStat') - ->will($this->returnValue($statMock)); + ->willReturn($statMock); $applyOperation->expects($this->once()) ->method('getJobGroup') ->with('tripod_php_testing', $jobTrackerId->__toString()) - ->will($this->returnValue($jobGroup)); + ->willReturn($jobGroup); $applyOperation->expects($this->never()) ->method('getTripod'); @@ -504,12 +508,12 @@ public function testApplyTableOperationDecrementsJobGroupForBatchOperations() $subject->expects($this->once()) ->method('getTripod') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $tripod->expects($this->once()) ->method('getComposite') ->with(OP_TABLES) - ->will($this->returnValue($tables)); + ->willReturn($tables); $tables->expects($this->once()) ->method('update') @@ -520,7 +524,7 @@ public function testApplyTableOperationDecrementsJobGroupForBatchOperations() $this->performJob($applyOperation); } - public function testApplyTableOperationCleanupIfAllGroupJobsComplete() + public function testApplyTableOperationCleanupIfAllGroupJobsComplete(): void { $this->setArgs(OP_TABLES, ['t_resource']); $applyOperation = $this->getMockBuilder(ApplyOperation::class) @@ -539,7 +543,7 @@ public function testApplyTableOperationCleanupIfAllGroupJobsComplete() $jobTrackerId = new ObjectId(); $applyOperation->args[ApplyOperation::TRACKING_KEY] = $jobTrackerId->__toString(); - $timestamp = new UTCDateTime(hexdec(substr($jobTrackerId, 0, 8)) * 1000); + $timestamp = new UTCDateTime($jobTrackerId->getTimestamp() * 1000); $jobGroup = $this->getMockBuilder(JobGroup::class) ->onlyMethods(['incrementJobCount']) @@ -549,7 +553,7 @@ public function testApplyTableOperationCleanupIfAllGroupJobsComplete() $jobGroup->expects($this->once()) ->method('incrementJobCount') ->with(-1) - ->will($this->returnValue(0)); + ->willReturn(0); $subject = $this->getMockBuilder(ImpactedSubject::class) ->onlyMethods(['getTripod']) @@ -582,21 +586,21 @@ public function testApplyTableOperationCleanupIfAllGroupJobsComplete() $applyOperation->expects($this->once()) ->method('createImpactedSubject') - ->will($this->returnValue($subject)); + ->willReturn($subject); $applyOperation->expects($this->exactly(3)) ->method('getStat') - ->will($this->returnValue($statMock)); + ->willReturn($statMock); $applyOperation->expects($this->once()) ->method('getJobGroup') ->with('tripod_php_testing', $jobTrackerId->__toString()) - ->will($this->returnValue($jobGroup)); + ->willReturn($jobGroup); $applyOperation->expects($this->once()) ->method('getTripod') ->with('tripod_php_testing', 'CBD_testing') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $statMock->expects($this->once()) ->method('increment') @@ -610,11 +614,11 @@ public function testApplyTableOperationCleanupIfAllGroupJobsComplete() $subject->expects($this->once()) ->method('getTripod') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $tripod->expects($this->exactly(2)) ->method('getTripodTables') - ->will($this->returnValue($tables)); + ->willReturn($tables); $tables->expects($this->once()) ->method('update') @@ -622,12 +626,12 @@ public function testApplyTableOperationCleanupIfAllGroupJobsComplete() $tables->expects($this->once()) ->method('deleteTableRowsByTableId') ->with('t_resource', $timestamp) - ->will($this->returnValue(4)); + ->willReturn(4); $this->performJob($applyOperation); } - public function testApplySearchOperation() + public function testApplySearchOperation(): void { $this->setArgs(OP_SEARCH, ['i_search_resource']); $applyOperation = $this->getMockBuilder(ApplyOperation::class) @@ -670,11 +674,11 @@ public function testApplySearchOperation() $applyOperation->expects($this->once()) ->method('createImpactedSubject') - ->will($this->returnValue($subject)); + ->willReturn($subject); $applyOperation->expects($this->exactly(3)) ->method('getStat') - ->will($this->returnValue($statMock)); + ->willReturn($statMock); $statMock->expects($this->once()) ->method('increment') @@ -688,12 +692,12 @@ public function testApplySearchOperation() $subject->expects($this->once()) ->method('getTripod') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $tripod->expects($this->once()) ->method('getComposite') ->with(OP_SEARCH) - ->will($this->returnValue($search)); + ->willReturn($search); $search->expects($this->once()) ->method('update') @@ -702,7 +706,7 @@ public function testApplySearchOperation() $this->performJob($applyOperation); } - public function testApplySearchOperationDecrementsJobGroupForBatchOperations() + public function testApplySearchOperationDecrementsJobGroupForBatchOperations(): void { $this->setArgs(OP_SEARCH, ['i_search_resource']); $applyOperation = $this->getMockBuilder(ApplyOperation::class) @@ -730,7 +734,7 @@ public function testApplySearchOperationDecrementsJobGroupForBatchOperations() $jobGroup->expects($this->once()) ->method('incrementJobCount') ->with(-1) - ->will($this->returnValue(2)); + ->willReturn(2); $subject = $this->getMockBuilder(ImpactedSubject::class) ->onlyMethods(['getTripod']) @@ -758,16 +762,16 @@ public function testApplySearchOperationDecrementsJobGroupForBatchOperations() $applyOperation->expects($this->once()) ->method('createImpactedSubject') - ->will($this->returnValue($subject)); + ->willReturn($subject); $applyOperation->expects($this->exactly(3)) ->method('getStat') - ->will($this->returnValue($statMock)); + ->willReturn($statMock); $applyOperation->expects($this->once()) ->method('getJobGroup') ->with('tripod_php_testing', $jobTrackerId->__toString()) - ->will($this->returnValue($jobGroup)); + ->willReturn($jobGroup); $applyOperation->expects($this->never()) ->method('getTripod'); @@ -784,12 +788,12 @@ public function testApplySearchOperationDecrementsJobGroupForBatchOperations() $subject->expects($this->once()) ->method('getTripod') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $tripod->expects($this->once()) ->method('getComposite') ->with(OP_SEARCH) - ->will($this->returnValue($search)); + ->willReturn($search); $search->expects($this->once()) ->method('update') @@ -798,7 +802,7 @@ public function testApplySearchOperationDecrementsJobGroupForBatchOperations() $this->performJob($applyOperation); } - public function testApplySearchOperationCleanupIfAllGroupJobsComplete() + public function testApplySearchOperationCleanupIfAllGroupJobsComplete(): void { $this->setArgs(OP_SEARCH, ['i_search_resource']); $applyOperation = $this->getMockBuilder(ApplyOperation::class) @@ -817,7 +821,7 @@ public function testApplySearchOperationCleanupIfAllGroupJobsComplete() $jobTrackerId = new ObjectId(); $applyOperation->args[ApplyOperation::TRACKING_KEY] = $jobTrackerId->__toString(); - $timestamp = new UTCDateTime(hexdec(substr($jobTrackerId, 0, 8)) * 1000); + $timestamp = new UTCDateTime($jobTrackerId->getTimestamp() * 1000); $jobGroup = $this->getMockBuilder(JobGroup::class) ->onlyMethods(['incrementJobCount']) @@ -827,7 +831,7 @@ public function testApplySearchOperationCleanupIfAllGroupJobsComplete() $jobGroup->expects($this->once()) ->method('incrementJobCount') ->with(-1) - ->will($this->returnValue(0)); + ->willReturn(0); $subject = $this->getMockBuilder(ImpactedSubject::class) ->onlyMethods(['getTripod']) @@ -860,26 +864,26 @@ public function testApplySearchOperationCleanupIfAllGroupJobsComplete() $applyOperation->expects($this->once()) ->method('createImpactedSubject') - ->will($this->returnValue($subject)); + ->willReturn($subject); $applyOperation->expects($this->exactly(3)) ->method('getStat') - ->will($this->returnValue($statMock)); + ->willReturn($statMock); $applyOperation->expects($this->once()) ->method('getJobGroup') ->with('tripod_php_testing', $jobTrackerId->__toString()) - ->will($this->returnValue($jobGroup)); + ->willReturn($jobGroup); $applyOperation->expects($this->once()) ->method('getTripod') ->with('tripod_php_testing', 'CBD_testing') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $applyOperation->expects($this->once()) ->method('getSearchProvider') ->with($tripod) - ->will($this->returnValue($searchProvider)); + ->willReturn($searchProvider); $statMock->expects($this->once()) ->method('increment') @@ -894,12 +898,12 @@ public function testApplySearchOperationCleanupIfAllGroupJobsComplete() $subject->expects($this->once()) ->method('getTripod') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $tripod->expects($this->once()) ->method('getComposite') ->with(OP_SEARCH) - ->will($this->returnValue($search)); + ->willReturn($search); $search->expects($this->once()) ->method('update') @@ -908,12 +912,12 @@ public function testApplySearchOperationCleanupIfAllGroupJobsComplete() $searchProvider->expects($this->once()) ->method('deleteSearchDocumentsByTypeId') ->with('i_search_resource', $timestamp) - ->will($this->returnValue(8)); + ->willReturn(8); $this->performJob($applyOperation); } - public function testCreateJobDefaultQueue() + public function testCreateJobDefaultQueue(): void { $impactedSubject = new ImpactedSubject( [_ID_RESOURCE => 'http://example.com/1', _ID_CONTEXT => 'http://talisaspire.com/'], @@ -944,7 +948,7 @@ public function testCreateJobDefaultQueue() $applyOperation->createJob([$impactedSubject]); } - public function testCreateJobUnreachableRedis() + public function testCreateJobUnreachableRedis(): void { $impactedSubject = new ImpactedSubject( [_ID_RESOURCE => 'http://example.com/1', _ID_CONTEXT => 'http://talisaspire.com/'], @@ -959,7 +963,7 @@ public function testCreateJobUnreachableRedis() ->getMock(); $e = new Exception('Connection to Redis failed after 1 failures.Last Error : (0) php_network_getaddresses: getaddrinfo failed: nodename nor servname provided, or not known'); - $applyOperation->expects($this->any())->method('enqueue')->will($this->throwException($e)); + $applyOperation->method('enqueue')->willThrowException($e); // expect 5 retries. Catch this with call to warning log $applyOperation->expects($this->exactly(5))->method('warningLog'); @@ -969,15 +973,16 @@ public function testCreateJobUnreachableRedis() try { $applyOperation->createJob([$impactedSubject]); } catch (JobException $e) { - $this->assertEquals('Exception queuing job - Connection to Redis failed after 1 failures.Last Error : (0) php_network_getaddresses: getaddrinfo failed: nodename nor servname provided, or not known', $e->getMessage()); + $this->assertSame('Exception queuing job - Connection to Redis failed after 1 failures.Last Error : (0) php_network_getaddresses: getaddrinfo failed: nodename nor servname provided, or not known', $e->getMessage()); $exceptionThrown = true; } + if (!$exceptionThrown) { $this->fail('Did not throw JobException'); } } - public function testCreateJobStatusFalse() + public function testCreateJobStatusFalse(): void { $impactedSubject = new ImpactedSubject( [_ID_RESOURCE => 'http://example.com/1', _ID_CONTEXT => 'http://talisaspire.com/'], @@ -988,11 +993,11 @@ public function testCreateJobStatusFalse() ); $applyOperation = $this->getMockBuilder(ApplyOperation::class) - ->onlyMethods(['enqueue', 'getJobStatus', 'warningLog']) + ->onlyMethods(['enqueue', 'hasJobStatus', 'warningLog']) ->getMock(); - $applyOperation->expects($this->any())->method('enqueue')->will($this->returnValue('sometoken')); - $applyOperation->expects($this->any())->method('getJobStatus')->will($this->returnValue(false)); + $applyOperation->method('enqueue')->willReturn('sometoken'); + $applyOperation->method('hasJobStatus')->willReturn(false); // expect 5 retries. Catch this with call to warning log $applyOperation->expects($this->exactly(5))->method('warningLog'); @@ -1002,15 +1007,16 @@ public function testCreateJobStatusFalse() try { $applyOperation->createJob([$impactedSubject]); } catch (JobException $e) { - $this->assertEquals('Exception queuing job - Could not retrieve status for queued job - job sometoken failed to tripod::apply', $e->getMessage()); + $this->assertSame('Exception queuing job - Could not retrieve status for queued job - job sometoken failed to tripod::apply', $e->getMessage()); $exceptionThrown = true; } + if (!$exceptionThrown) { $this->fail('Did not throw JobException'); } } - public function testCreateJobSpecifyQueue() + public function testCreateJobSpecifyQueue(): void { $impactedSubject = new ImpactedSubject( [_ID_RESOURCE => 'http://example.com/1', _ID_CONTEXT => 'http://talisaspire.com/'], @@ -1046,7 +1052,8 @@ public function testCreateJobSpecifyQueue() /** * Sets job arguments. * - * @param mixed $operation + * @param mixed $operation + * @param string[] $specTypes */ protected function setArgs($operation = OP_VIEWS, array $specTypes = []) { diff --git a/test/unit/mongo/ConfigGeneratorTest.php b/test/unit/mongo/ConfigGeneratorTest.php index e24d3282..bd43f2a2 100644 --- a/test/unit/mongo/ConfigGeneratorTest.php +++ b/test/unit/mongo/ConfigGeneratorTest.php @@ -1,5 +1,7 @@ config); } - public function testCreateFromConfig() + public function testCreateFromConfig(): void { /** @var TestConfigGenerator $instance */ $instance = Config::getInstance(); $this->assertInstanceOf(TestConfigGenerator::class, $instance); $this->assertInstanceOf(Tripod\Mongo\Config::class, $instance); $this->assertInstanceOf(ITripodConfigSerializer::class, $instance); - $this->assertEquals( + $this->assertSame( ['CBD_testing', 'CBD_test_related_content', 'CBD_testing_2'], $instance->getPods('tripod_php_testing') ); } - public function testSerializeConfig() + public function testSerializeConfig(): void { /** @var TestConfigGenerator $instance */ $instance = Config::getInstance(); $this->assertEquals($this->config, $instance->serialize()); } - public function testConfigGeneratorsSerializedInDiscoverJobs() + public function testConfigGeneratorsSerializedInDiscoverJobs(): void { $originalGraph = new ExtendedGraph(); $originalGraph->add_resource_triple('http://example.com/1', RDF_TYPE, RDFS_CLASS); $newGraph = new ExtendedGraph(); $newGraph->add_resource_triple('http://example.com/1', RDF_TYPE, OWL_CLASS); + $subjectsAndPredicatesOfChange = ['http://example.com/1' => [RDF_TYPE]]; $tripod = $this->getMockBuilder(Driver::class) @@ -78,13 +81,11 @@ public function testConfigGeneratorsSerializedInDiscoverJobs() ->onlyMethods(['createJob']) ->getMock(); - $tripod->expects($this->once())->method('getDataUpdater')->will($this->returnValue($updates)); - $updates->expects($this->once())->method('getDiscoverImpactedSubjects')->will($this->returnValue($discoverJob)); + $tripod->expects($this->once())->method('getDataUpdater')->willReturn($updates); + $updates->expects($this->once())->method('getDiscoverImpactedSubjects')->willReturn($discoverJob); - $updates->expects($this->once())->method('storeChanges')->will( - $this->returnValue( - ['transaction_id' => uniqid(), 'subjectsAndPredicatesOfChange' => $subjectsAndPredicatesOfChange] - ) + $updates->expects($this->once())->method('storeChanges')->willReturn( + ['transaction_id' => uniqid(), 'subjectsAndPredicatesOfChange' => $subjectsAndPredicatesOfChange] ); $discoverJob->expects($this->once())->method('createJob') @@ -103,7 +104,7 @@ public function testConfigGeneratorsSerializedInDiscoverJobs() ); } - public function testSerializedConfigGeneratorsSentToApplyJobs() + public function testSerializedConfigGeneratorsSentToApplyJobs(): void { $subjectsAndPredicatesOfChange = ['http://example.com/1' => [RDF_TYPE]]; $impactedSubjects = [ @@ -136,9 +137,9 @@ public function testSerializedConfigGeneratorsSentToApplyJobs() $tripod->expects($this->once())->method('getComposite') ->with(OP_VIEWS) - ->will($this->returnValue($views)); + ->willReturn($views); - $views->expects($this->once())->method('getImpactedSubjects')->will($this->returnValue($impactedSubjects)); + $views->expects($this->once())->method('getImpactedSubjects')->willReturn($impactedSubjects); $discoverJob = $this->getMockBuilder(DiscoverImpactedSubjects::class) ->onlyMethods(['getTripod', 'getApplyOperation']) @@ -149,9 +150,9 @@ public function testSerializedConfigGeneratorsSentToApplyJobs() ->setMockClassName('ApplyOperation_TestConfigGenerator') ->getMock(); $discoverJob->args = $jobArgs; - $discoverJob->job = (object) ['payload' => ['id' => uniqid()]]; - $discoverJob->expects($this->once())->method('getTripod')->will($this->returnValue($tripod)); - $discoverJob->expects($this->once())->method('getApplyOperation')->will($this->returnValue($applyJob)); + $discoverJob->job = new Resque_Job('discover_queue', ['id' => uniqid()]); + $discoverJob->expects($this->once())->method('getTripod')->willReturn($tripod); + $discoverJob->expects($this->once())->method('getApplyOperation')->willReturn($applyJob); $configInstance = Config::getInstance(); $applyJob->expects($this->once())->method('submitJob') ->with( diff --git a/test/unit/mongo/DateUtilTest.php b/test/unit/mongo/DateUtilTest.php index b6e94474..09c77b45 100644 --- a/test/unit/mongo/DateUtilTest.php +++ b/test/unit/mongo/DateUtilTest.php @@ -1,19 +1,22 @@ getMongoDate(); $_id = [ 'r' => 'http://talisaspire.com/resources/testEtag' . microtime(false), - 'c' => 'http://talisaspire.com/']; + 'c' => 'http://talisaspire.com/', + ]; $doc = [ '_id' => $_id, 'dct:title' => ['l' => 'etag'], @@ -29,17 +32,18 @@ public function testGetMongoDateWithNoParam() $date = DateUtil::getMongoDate(); $this->assertInstanceOf(UTCDateTime::class, $date); - $this->assertEquals(13, strlen($date->__toString())); + $this->assertSame(13, strlen($date->__toString())); } - public function testGetMongoDateWithParam() + public function testGetMongoDateWithParam(): void { $config = Config::getInstance(); $updatedAt = (new DateUtil())->getMongoDate(); $_id = [ 'r' => 'http://talisaspire.com/resources/testEtag' . microtime(false), - 'c' => 'http://talisaspire.com/']; + 'c' => 'http://talisaspire.com/', + ]; $doc = [ '_id' => $_id, 'dct:title' => ['l' => 'etag'], @@ -56,7 +60,7 @@ public function testGetMongoDateWithParam() $date = DateUtil::getMongoDate($time); $this->assertInstanceOf(UTCDateTime::class, $date); - $this->assertEquals(13, strlen($date->__toString())); + $this->assertSame(13, strlen($date->__toString())); $this->assertEquals($time, $date->__toString()); } } diff --git a/test/unit/mongo/DiscoverImpactedSubjectsTest.php b/test/unit/mongo/DiscoverImpactedSubjectsTest.php index 69547b43..bf06630a 100644 --- a/test/unit/mongo/DiscoverImpactedSubjectsTest.php +++ b/test/unit/mongo/DiscoverImpactedSubjectsTest.php @@ -1,5 +1,7 @@ setArgs(); unset($this->args['tripodConfig']); @@ -26,7 +28,7 @@ public function testMandatoryArgTripodConfig() $this->performJob($job); } - public function testMandatoryArgStoreName() + public function testMandatoryArgStoreName(): void { $this->setArgs(); unset($this->args['storeName']); @@ -38,7 +40,7 @@ public function testMandatoryArgStoreName() $this->performJob($job); } - public function testMandatoryArgPodName() + public function testMandatoryArgPodName(): void { $this->setArgs(); unset($this->args['podName']); @@ -50,7 +52,7 @@ public function testMandatoryArgPodName() $this->performJob($job); } - public function testMandatoryArgChanges() + public function testMandatoryArgChanges(): void { $this->setArgs(); unset($this->args['changes']); @@ -62,7 +64,7 @@ public function testMandatoryArgChanges() $this->performJob($job); } - public function testMandatoryArgOperations() + public function testMandatoryArgOperations(): void { $this->setArgs(); unset($this->args['operations']); @@ -74,7 +76,7 @@ public function testMandatoryArgOperations() $this->performJob($job); } - public function testMandatoryArgContextAlias() + public function testMandatoryArgContextAlias(): void { $this->setArgs(); unset($this->args['contextAlias']); @@ -86,7 +88,7 @@ public function testMandatoryArgContextAlias() $this->performJob($job); } - public function testSubmitApplyOperationsJob() + public function testSubmitApplyOperationsJob(): void { $this->setArgs(); @@ -124,13 +126,11 @@ public function testSubmitApplyOperationsJob() $tripod->expects($this->exactly(3)) ->method('getComposite') - ->will($this->returnValueMap( - [ - [OP_VIEWS, $views], - [OP_TABLES, $tables], - [OP_SEARCH, $search], - ] - )); + ->willReturnMap([ + [OP_VIEWS, $views], + [OP_TABLES, $tables], + [OP_SEARCH, $search], + ]); $discoverImpactedSubjects = $this->getMockBuilder(DiscoverImpactedSubjects::class) ->onlyMethods(['getTripod', 'getApplyOperation', 'getStat']) @@ -145,7 +145,7 @@ public function testSubmitApplyOperationsJob() $discoverImpactedSubjects->expects($this->once()) ->method('getTripod') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $discoverImpactedSubjects->args = $this->args; $discoverImpactedSubjects->job = new Resque_Job('queue', ['id' => uniqid()]); @@ -168,9 +168,7 @@ public function testSubmitApplyOperationsJob() $views->expects($this->once()) ->method('getImpactedSubjects') ->with($this->args['changes'], $this->args['contextAlias']) - ->will($this->returnValue( - [$viewSubject] - )); + ->willReturn([$viewSubject]); $tableSubjects = [ new ImpactedSubject( @@ -198,22 +196,20 @@ public function testSubmitApplyOperationsJob() $tables->expects($this->once()) ->method('getImpactedSubjects') ->with($this->args['changes'], $this->args['contextAlias']) - ->will($this->returnValue( - $tableSubjects - )); + ->willReturn($tableSubjects); $search->expects($this->once()) ->method('getImpactedSubjects') ->with($this->args['changes'], $this->args['contextAlias']) - ->will($this->returnValue([])); + ->willReturn([]); $discoverImpactedSubjects->expects($this->exactly(2)) ->method('getApplyOperation') - ->will($this->returnValue($applyOperation)); + ->willReturn($applyOperation); $discoverImpactedSubjects->expects($this->exactly(5)) ->method('getStat') - ->will($this->returnValue($statMock)); + ->willReturn($statMock); $applyOperation->expects($this->exactly(2)) ->method('createJob') @@ -246,7 +242,7 @@ public function testSubmitApplyOperationsJob() $this->performJob($discoverImpactedSubjects); } - public function testCreateJobDefaultQueue() + public function testCreateJobDefaultQueue(): void { $labeller = new Labeller(); @@ -280,7 +276,7 @@ public function testCreateJobDefaultQueue() $discoverImpactedSubjects->createJob($jobData); } - public function testCreateJobSpecifyQueue() + public function testCreateJobSpecifyQueue(): void { $labeller = new Labeller(); @@ -316,7 +312,7 @@ public function testCreateJobSpecifyQueue() $discoverImpactedSubjects->createJob($jobData, $queueName); } - public function testManualQueueNamePersistsThroughJob() + public function testManualQueueNamePersistsThroughJob(): void { $discoverImpactedSubjects = $this->getMockBuilder(DiscoverImpactedSubjects::class) ->onlyMethods(['getTripod', 'getApplyOperation']) @@ -360,13 +356,11 @@ public function testManualQueueNamePersistsThroughJob() $tripod->expects($this->exactly(3)) ->method('getComposite') - ->will($this->returnValueMap( - [ - [OP_VIEWS, $views], - [OP_TABLES, $tables], - [OP_SEARCH, $search], - ] - )); + ->willReturnMap([ + [OP_VIEWS, $views], + [OP_TABLES, $tables], + [OP_SEARCH, $search], + ]); $applyOperation = $this->getMockBuilder(ApplyOperation::class) ->onlyMethods(['createJob']) @@ -385,9 +379,7 @@ public function testManualQueueNamePersistsThroughJob() $views->expects($this->once()) ->method('getImpactedSubjects') ->with($this->args['changes'], $this->args['contextAlias']) - ->will($this->returnValue( - [$viewSubject] - )); + ->willReturn([$viewSubject]); $tableSubject = new ImpactedSubject( [ @@ -403,22 +395,20 @@ public function testManualQueueNamePersistsThroughJob() $tables->expects($this->once()) ->method('getImpactedSubjects') ->with($this->args['changes'], $this->args['contextAlias']) - ->will($this->returnValue( - [$tableSubject] - )); + ->willReturn([$tableSubject]); $search->expects($this->once()) ->method('getImpactedSubjects') ->with($this->args['changes'], $this->args['contextAlias']) - ->will($this->returnValue([])); + ->willReturn([]); $discoverImpactedSubjects->expects($this->once()) ->method('getTripod') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $discoverImpactedSubjects->expects($this->exactly(2)) ->method('getApplyOperation') - ->will($this->returnValue($applyOperation)); + ->willReturn($applyOperation); $applyOperation->expects($this->exactly(2)) ->method('createJob') @@ -436,7 +426,7 @@ public function testManualQueueNamePersistsThroughJob() $this->performJob($discoverImpactedSubjects); } - public function testDiscoverOperationWillSubmitApplyOperationForDistinctQueues() + public function testDiscoverOperationWillSubmitApplyOperationForDistinctQueues(): void { $config = Config::getConfig(); @@ -632,20 +622,20 @@ public function testDiscoverOperationWillSubmitApplyOperationForDistinctQueues() $tables->expects($this->once()) ->method('getImpactedSubjects') ->with($this->args['changes'], $this->args['contextAlias']) - ->will($this->returnValue([$tableSubject1, $tableSubject2])); + ->willReturn([$tableSubject1, $tableSubject2]); $tripod->expects($this->once()) ->method('getComposite') ->with(OP_TABLES) - ->will($this->returnValue($tables)); + ->willReturn($tables); $discoverImpactedSubjects->expects($this->once()) ->method('getTripod') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $discoverImpactedSubjects->expects($this->exactly(3)) ->method('getApplyOperation') - ->will($this->returnValue($applyOperation)); + ->willReturn($applyOperation); $applyOperation->expects($this->exactly(3)) ->method('createJob') @@ -667,7 +657,7 @@ public function testDiscoverOperationWillSubmitApplyOperationForDistinctQueues() $this->performJob($discoverImpactedSubjects); } - public function testManuallySpecifiedQueueWillOverrideQueuesDefinedInConfig() + public function testManuallySpecifiedQueueWillOverrideQueuesDefinedInConfig(): void { $config = Config::getConfig(); @@ -806,28 +796,26 @@ public function testManuallySpecifiedQueueWillOverrideQueuesDefinedInConfig() $tables->expects($this->once()) ->method('getImpactedSubjects') ->with($this->args['changes'], $this->args['contextAlias']) - ->will($this->returnValue($tableSubjects)); + ->willReturn($tableSubjects); $tripod->expects($this->once()) ->method('getComposite') ->with(OP_TABLES) - ->will($this->returnValue($tables)); + ->willReturn($tables); $discoverImpactedSubjects->expects($this->once()) ->method('getTripod') - ->will($this->returnValue($tripod)); + ->willReturn($tripod); $discoverImpactedSubjects->expects($this->once()) ->method('getApplyOperation') - ->will($this->returnValue($applyOperation)); + ->willReturn($applyOperation); $applyOperation->expects($this->once()) ->method('createJob') - ->withConsecutive( - [ - $tableSubjects, - $args['queue'], - ] + ->with( + $tableSubjects, + $args['queue'], ); $this->performJob($discoverImpactedSubjects); diff --git a/test/unit/mongo/EnsureIndexesTest.php b/test/unit/mongo/EnsureIndexesTest.php index 47c2b2f8..b35e0c86 100644 --- a/test/unit/mongo/EnsureIndexesTest.php +++ b/test/unit/mongo/EnsureIndexesTest.php @@ -1,5 +1,7 @@ args = $this->args; $job->job = new Resque_Job('queue', ['id' => uniqid()]); unset($job->args[$argument]); $this->expectException(Exception::class); - $this->expectExceptionMessage("Argument {$argumentName} was not present in supplied job args for job Tripod\\Mongo\\Jobs\\EnsureIndexes"); + $this->expectExceptionMessage(sprintf('Argument %s was not present in supplied job args for job Tripod\Mongo\Jobs\EnsureIndexes', $argumentName)); $this->performJob($job); } /** * Data provider for testMandatoryArgs. - * - * @return array */ - public function mandatoryArgDataProvider() + public function mandatoryArgDataProvider(): iterable { - return [ - ['tripodConfig', 'tripodConfig or tripodConfigGenerator'], - ['storeName'], - ['reindex'], - ['background'], - ]; + yield ['tripodConfig', 'tripodConfig or tripodConfigGenerator']; + yield ['storeName']; + yield ['reindex']; + yield ['background']; } /** @@ -71,7 +67,7 @@ public function mandatoryArgDataProvider() * * @group ensure-indexes */ - public function testSuccessfullyEnsureIndexesJob() + public function testSuccessfullyEnsureIndexesJob(): void { $job = $this->createMockJob(); $job->args = $this->createDefaultArguments(); @@ -85,7 +81,7 @@ public function testSuccessfullyEnsureIndexesJob() * * @group ensure-indexes */ - public function testEnsureIndexesJobThrowsErrorWhenCreatingIndexes() + public function testEnsureIndexesJobThrowsErrorWhenCreatingIndexes(): void { $job = $this->createMockJob(); $job->args = $this->createDefaultArguments(); @@ -100,7 +96,7 @@ public function testEnsureIndexesJobThrowsErrorWhenCreatingIndexes() * test that calling the create job method on the ensureindexes job class * will use the default queue name. */ - public function testEnsureIndexesCreateJobDefaultQueue() + public function testEnsureIndexesCreateJobDefaultQueue(): void { $jobData = [ 'storeName' => 'tripod_php_testing', @@ -126,9 +122,9 @@ public function testEnsureIndexesCreateJobDefaultQueue() * test that calling the create job method on the ensureindexes job class * will throw the expected exception if redis is unreachable. */ - public function testEnsureIndexesCreateJobUnreachableRedis() + public function testEnsureIndexesCreateJobUnreachableRedis(): void { - $jobData = [ + [ 'storeName' => 'tripod_php_testing', 'tripodConfig' => Config::getConfig(), 'reindex' => false, @@ -144,7 +140,7 @@ public function testEnsureIndexesCreateJobUnreachableRedis() // this is called 6 times because after the first attempt fails it will // retry 5 times. - $job->expects($this->exactly(6))->method('enqueue')->will($this->throwException($e)); + $job->expects($this->exactly(6))->method('enqueue')->willThrowException($e); // expect 5 retries. Catch this with call to warning log $job->expects($this->exactly(5))->method('warningLog'); @@ -158,9 +154,9 @@ public function testEnsureIndexesCreateJobUnreachableRedis() * test that calling the create job method on the ensureindexes job class * will throw the expected exception if the job fails. */ - public function testEnsureIndexesCreateJobStatusFalse() + public function testEnsureIndexesCreateJobStatusFalse(): void { - $jobData = [ + [ 'storeName' => 'tripod_php_testing', 'tripodConfig' => Config::getConfig(), 'reindex' => false, @@ -168,13 +164,13 @@ public function testEnsureIndexesCreateJobStatusFalse() ]; $job = $this->getMockBuilder(EnsureIndexes::class) - ->onlyMethods(['warningLog', 'enqueue', 'getJobStatus']) + ->onlyMethods(['warningLog', 'enqueue', 'hasJobStatus']) ->getMock(); // both of these methods will be called 6 times because after the first attempt fails it will // retry 5 times. - $job->expects($this->exactly(6))->method('enqueue')->will($this->returnValue('sometoken')); - $job->expects($this->exactly(6))->method('getJobStatus')->will($this->returnValue(false)); + $job->expects($this->exactly(6))->method('enqueue')->willReturn('sometoken'); + $job->expects($this->exactly(6))->method('hasJobStatus')->willReturn(false); // expect 5 retries. Catch this with call to warning log $job->expects($this->exactly(5))->method('warningLog'); @@ -187,7 +183,7 @@ public function testEnsureIndexesCreateJobStatusFalse() * test that calling the create job method on the ensureindexes job class * will use the queue name specified. */ - public function testEnsureIndexesCreateJobSpecifyQueue() + public function testEnsureIndexesCreateJobSpecifyQueue(): void { $jobData = [ 'storeName' => 'tripod_php_testing', @@ -210,26 +206,18 @@ public function testEnsureIndexesCreateJobSpecifyQueue() $job->createJob('tripod_php_testing', false, true, $queueName); } + // HELPER METHODS BELOW HERE /** * Creates a simple mock EnsureIndexes Job. * - * @param array list of methods to stub - * @param mixed $methods - * * @return EnsureIndexes&MockObject */ - protected function createMockJob($methods = []) + protected function createMockJob(array $methods = ['getIndexUtils', 'submitJob', 'warningLog', 'enqueue', 'hasJobStatus']): MockObject { - $methodsToStub = ['getIndexUtils', 'submitJob', 'warningLog', 'enqueue', 'getJobStatus']; - - if (!empty($methods)) { - $methodsToStub = $methods; - } - $mockEnsureIndexesJob = $this->getMockBuilder(EnsureIndexes::class) - ->onlyMethods($methodsToStub) + ->onlyMethods($methods) ->setMockClassName('MockEnsureIndexes') ->getMock(); $mockEnsureIndexesJob->job = new Resque_Job('queue', ['id' => uniqid()]); @@ -240,9 +228,9 @@ protected function createMockJob($methods = []) /** * Returns default arguments for a EnsureIndexes Job. * - * @return array + * @return array */ - protected function createDefaultArguments() + protected function createDefaultArguments(): array { return [ 'tripodConfig' => Config::getConfig(), @@ -267,7 +255,7 @@ protected function jobSuccessfullyEnsuresIndexes($job) $job->expects($this->once()) ->method('getIndexUtils') - ->will($this->returnValue($mockIndexUtils)); + ->willReturn($mockIndexUtils); } /** @@ -282,10 +270,10 @@ protected function jobThrowsExceptionWhenEnsuringIndexes($job) $mockIndexUtils->expects($this->once()) ->method('ensureIndexes') ->with(false, 'tripod_php_testing', true) - ->will($this->throwException(new Exception('Ensuring index failed'))); + ->willThrowException(new Exception('Ensuring index failed')); $job->expects($this->once()) ->method('getIndexUtils') - ->will($this->returnValue($mockIndexUtils)); + ->willReturn($mockIndexUtils); } } diff --git a/test/unit/mongo/IndexUtilsTest.php b/test/unit/mongo/IndexUtilsTest.php index 1e9cf604..c58643fa 100644 --- a/test/unit/mongo/IndexUtilsTest.php +++ b/test/unit/mongo/IndexUtilsTest.php @@ -1,12 +1,15 @@ createMockConfig(); $collection = $this->createMockCollection(); @@ -23,7 +26,7 @@ public function testCBDCollectionIndexesAreCreated() $indexUtils->ensureIndexes(false, 'tripod_php_testing', true); } - public function testCBDCollectionIndexesAreCreatedWithIndexOptions() + public function testCBDCollectionIndexesAreCreatedWithIndexOptions(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -42,7 +45,7 @@ public function testCBDCollectionIndexesAreCreatedWithIndexOptions() $indexUtils->ensureIndexes(false, 'tripod_php_testing', true); } - public function testCBDCollectionIndexesAreCreatedInForeground() + public function testCBDCollectionIndexesAreCreatedInForeground(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -59,7 +62,7 @@ public function testCBDCollectionIndexesAreCreatedInForeground() $indexUtils->ensureIndexes(false, 'tripod_php_testing', false); } - public function testCBDCollectionIndexesAreCreatedInForegroundWithIndexOptions() + public function testCBDCollectionIndexesAreCreatedInForegroundWithIndexOptions(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -78,7 +81,7 @@ public function testCBDCollectionIndexesAreCreatedInForegroundWithIndexOptions() $indexUtils->ensureIndexes(false, 'tripod_php_testing', false); } - public function testCBDCollectionIndexesAreReindexed() + public function testCBDCollectionIndexesAreReindexed(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -95,7 +98,7 @@ public function testCBDCollectionIndexesAreReindexed() $indexUtils->ensureIndexes(true, 'tripod_php_testing', true); } - public function testCBDCollectionIndexesAreReindexedWithIndexOptions() + public function testCBDCollectionIndexesAreReindexedWithIndexOptions(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -114,7 +117,7 @@ public function testCBDCollectionIndexesAreReindexedWithIndexOptions() $indexUtils->ensureIndexes(true, 'tripod_php_testing', true); } - public function testViewIndexesAreCreated() + public function testViewIndexesAreCreated(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -131,7 +134,7 @@ public function testViewIndexesAreCreated() $indexUtils->ensureIndexes(false, 'tripod_php_testing', true); } - public function testViewIndexesAreCreatedInForeground() + public function testViewIndexesAreCreatedInForeground(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -148,7 +151,7 @@ public function testViewIndexesAreCreatedInForeground() $indexUtils->ensureIndexes(false, 'tripod_php_testing', false); } - public function testViewIndexesAreReindexed() + public function testViewIndexesAreReindexed(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -165,7 +168,7 @@ public function testViewIndexesAreReindexed() $indexUtils->ensureIndexes(true, 'tripod_php_testing', true); } - public function testTableIndexesAreCreated() + public function testTableIndexesAreCreated(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -182,7 +185,7 @@ public function testTableIndexesAreCreated() $indexUtils->ensureIndexes(false, 'tripod_php_testing', true); } - public function testTableIndexesAreCreatedInForeground() + public function testTableIndexesAreCreatedInForeground(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -199,7 +202,7 @@ public function testTableIndexesAreCreatedInForeground() $indexUtils->ensureIndexes(false, 'tripod_php_testing', false); } - public function testTableIndexesAreReindexed() + public function testTableIndexesAreReindexed(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -216,7 +219,7 @@ public function testTableIndexesAreReindexed() $indexUtils->ensureIndexes(true, 'tripod_php_testing', true); } - public function testSearchDocIndexesAreCreated() + public function testSearchDocIndexesAreCreated(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -233,7 +236,7 @@ public function testSearchDocIndexesAreCreated() $indexUtils->ensureIndexes(false, 'tripod_php_testing', true); } - public function testSearchDocIndexesAreCreatedInForeground() + public function testSearchDocIndexesAreCreatedInForeground(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -250,7 +253,7 @@ public function testSearchDocIndexesAreCreatedInForeground() $indexUtils->ensureIndexes(false, 'tripod_php_testing', false); } - public function testSearchDocIndexesAreReindexed() + public function testSearchDocIndexesAreReindexed(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -267,7 +270,7 @@ public function testSearchDocIndexesAreReindexed() $indexUtils->ensureIndexes(true, 'tripod_php_testing', true); } - public function testIndexesAreDroppedOnlyOncePerCollectionWhenReindexed() + public function testIndexesAreDroppedOnlyOncePerCollectionWhenReindexed(): void { $config = $this->createMockConfig(); $collection = $this->createMockCollection(); @@ -292,7 +295,7 @@ public function testIndexesAreDroppedOnlyOncePerCollectionWhenReindexed() * * @return IndexUtils&MockObject mocked IndexUtil object */ - protected function createMockIndexUtils($mockConfig) + protected function createMockIndexUtils($mockConfig): MockObject { $mockIndexUtils = $this->getMockBuilder(IndexUtils::class) ->onlyMethods(['getConfig']) @@ -300,7 +303,7 @@ protected function createMockIndexUtils($mockConfig) $mockIndexUtils->expects($this->once()) ->method('getConfig') - ->will($this->returnValue($mockConfig)); + ->willReturn($mockConfig); return $mockIndexUtils; } @@ -310,9 +313,9 @@ protected function createMockIndexUtils($mockConfig) * * @return Collection&MockObject mock Collection object */ - protected function createMockCollection() + protected function createMockCollection(): MockObject { - return $this->getMockBuilder(MongoDB\Collection::class) + return $this->getMockBuilder(Collection::class) ->onlyMethods(['createIndex', 'dropIndexes']) ->setConstructorArgs([ new Manager('mongodb://fake:27017'), @@ -327,7 +330,7 @@ protected function createMockCollection() * * @return MockObject&TripodTestConfig mock Config object */ - protected function createMockConfig() + protected function createMockConfig(): MockObject { return $this->getMockBuilder(TripodTestConfig::class) ->onlyMethods([ @@ -344,12 +347,12 @@ protected function createMockConfig() * @param MockObject&TripodTestConfig $mockConfig mock Config object * @param Collection&MockObject $mockCollection mock Collection object */ - protected function getCollectionForCBDShouldBeCalled_n_Times($callCount, $mockConfig, $mockCollection) + protected function getCollectionForCBDShouldBeCalled_n_Times(int $callCount, $mockConfig, $mockCollection) { $mockConfig->expects($this->exactly($callCount)) ->method('getCollectionForCBD') ->with('tripod_php_testing', 'CBD_testing') - ->will($this->returnValue($mockCollection)); + ->willReturn($mockCollection); } /** @@ -357,12 +360,12 @@ protected function getCollectionForCBDShouldBeCalled_n_Times($callCount, $mockCo * @param MockObject&TripodTestConfig $mockConfig mock Config object * @param Collection&MockObject $mockCollection mock Collection object */ - protected function getCollectionForViewShouldBeCalled_n_Times($callCount, $mockConfig, $mockCollection) + protected function getCollectionForViewShouldBeCalled_n_Times(int $callCount, $mockConfig, $mockCollection) { $mockConfig->expects($this->exactly($callCount)) ->method('getCollectionForView') ->with('tripod_php_testing', 'v_testview') - ->will($this->returnValue($mockCollection)); + ->willReturn($mockCollection); } /** @@ -370,12 +373,12 @@ protected function getCollectionForViewShouldBeCalled_n_Times($callCount, $mockC * @param MockObject&TripodTestConfig $mockConfig mock Config object * @param Collection&MockObject $mockCollection mock Collection object */ - protected function getCollectionForTableShouldBeCalled_n_Times($callCount, $mockConfig, $mockCollection) + protected function getCollectionForTableShouldBeCalled_n_Times(int $callCount, $mockConfig, $mockCollection) { $mockConfig->expects($this->exactly($callCount)) ->method('getCollectionForTable') ->with('tripod_php_testing', 't_testtable') - ->will($this->returnValue($mockCollection)); + ->willReturn($mockCollection); } /** @@ -383,12 +386,12 @@ protected function getCollectionForTableShouldBeCalled_n_Times($callCount, $mock * @param MockObject&TripodTestConfig $mockConfig mock Config object * @param Collection&MockObject $mockCollection mock Collection object */ - protected function getCollectionForSearchDocShouldBeCalled_n_Times($callCount, $mockConfig, $mockCollection) + protected function getCollectionForSearchDocShouldBeCalled_n_Times(int $callCount, $mockConfig, $mockCollection) { $mockConfig->expects($this->exactly($callCount)) ->method('getCollectionForSearchDocument') ->with('tripod_php_testing', 'i_search_something') - ->will($this->returnValue($mockCollection)); + ->willReturn($mockCollection); } /** @@ -455,6 +458,7 @@ protected function getCollectionForCBDShouldNeverBeCalled($mockConfig) * * @param Collection&MockObject $mockCollection mock Collection object * @param bool $background create indexes in the background + * @param array $indexOptions */ protected function oneCustomAndThreeInternalTripodCBDIndexesShouldBeCreated($mockCollection, $background = true, array $indexOptions = []) { @@ -538,7 +542,8 @@ protected function threeInternalTripodSearchDocIndexesShouldBeCreated($mockColle * This is a minimal config used to assert what should happen when ensuring * indexes for a CBD collection. * - * @param MockObject&TripodTestConfig $mockConfig mock Config object + * @param MockObject&TripodTestConfig $mockConfig mock Config object + * @param array $indexOptions */ protected function setConfigForCBDIndexes($mockConfig, array $indexOptions = []) { @@ -564,7 +569,7 @@ protected function setConfigForCBDIndexes($mockConfig, array $indexOptions = []) ], ]; - if (empty($indexOptions)) { + if ($indexOptions === []) { $config['stores']['tripod_php_testing']['pods']['CBD_testing']['indexes'] = [ 'rdf_type' => [ 'rdf:type.u' => 1, diff --git a/test/unit/mongo/JobBaseTest.php b/test/unit/mongo/JobBaseTest.php index 9b15fb29..3c272a5b 100644 --- a/test/unit/mongo/JobBaseTest.php +++ b/test/unit/mongo/JobBaseTest.php @@ -1,11 +1,13 @@ args = $this->getArgs(); @@ -14,7 +16,10 @@ public function testGetTripodConfig() $this->assertInstanceOf(IConfigInstance::class, $job->getTripodConfig()); } - protected function getArgs() + /** + * @return array + */ + protected function getArgs(): array { return [ 'tripodConfig' => Config::getConfig(), diff --git a/test/unit/mongo/MongoGraphTest.php b/test/unit/mongo/MongoGraphTest.php index 88575576..16921b4a 100644 --- a/test/unit/mongo/MongoGraphTest.php +++ b/test/unit/mongo/MongoGraphTest.php @@ -1,6 +1,9 @@ assertEquals('dct:title', $g->uri_to_qname('http://purl.org/dc/terms/title')); + $this->assertSame('dct:title', $g->uri_to_qname('http://purl.org/dc/terms/title')); } - public function testUriToQNameOnUnRegisteredNS() + public function testUriToQNameOnUnRegisteredNS(): void { $this->expectException(LabellerException::class); $this->expectExceptionMessage('Could not label: http://someunregisteredns/'); @@ -25,7 +28,7 @@ public function testUriToQNameOnUnRegisteredNS() $g->uri_to_qname('http://someunregisteredns/title'); } - public function testQNameToUriOnUnRegisteredNS() + public function testQNameToUriOnUnRegisteredNS(): void { $this->expectException(LabellerException::class); $this->expectExceptionMessage('Could not label: someunregisteredns:title'); @@ -33,7 +36,7 @@ public function testQNameToUriOnUnRegisteredNS() $g->qname_to_uri('someunregisteredns:title'); } - public function testToNQuadsThrowsInvalidArgumentException() + public function testToNQuadsThrowsInvalidArgumentException(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('You must specify the context when serializing to nquads'); @@ -41,7 +44,7 @@ public function testToNQuadsThrowsInvalidArgumentException() $g->to_nquads(null); } - public function testToNQuads() + public function testToNQuads(): void { $g = new MongoGraph(); $g->add_literal_triple('http://example.com/1', $g->qname_to_uri('dct:title'), 'some literal title'); @@ -49,10 +52,10 @@ public function testToNQuads() $expected = " \"some literal title\" . .\n"; - $this->assertEquals($expected, $g->to_nquads(Config::getInstance()->getDefaultContextAlias())); + $this->assertSame($expected, $g->to_nquads(Config::getInstance()->getDefaultContextAlias())); } - public function testToNQuadsTwoGraphsWithDifferentContext() + public function testToNQuadsTwoGraphsWithDifferentContext(): void { $g = new MongoGraph(); $g->add_literal_triple('http://example.com/1', $g->qname_to_uri('dct:title'), 'some literal title'); @@ -60,7 +63,7 @@ public function testToNQuadsTwoGraphsWithDifferentContext() $expected = " \"some literal title\" . .\n"; - $this->assertEquals($expected, $g->to_nquads('http://talisaspire.com/')); + $this->assertSame($expected, $g->to_nquads('http://talisaspire.com/')); $g = new MongoGraph(); $g->add_literal_triple('http://example.com/2', $g->qname_to_uri('dct:title'), 'some literal title'); @@ -68,18 +71,10 @@ public function testToNQuadsTwoGraphsWithDifferentContext() $expected = " \"some literal title\" . .\n"; - $this->assertEquals($expected, $g->to_nquads('http://wibble.talisaspire.com/')); - } - - public function testAddTripodArrayThrowsException() - { - $this->expectException(Exception::class); - $this->expectExceptionMessage('Value passed to add_tripod_array is not of type array'); - $g = new MongoGraph(); - $g->add_tripod_array(null); + $this->assertSame($expected, $g->to_nquads('http://wibble.talisaspire.com/')); } - public function testAddTripodArraySingleDoc() + public function testAddTripodArraySingleDoc(): void { $doc = [ '_id' => ['r' => 'http://talisaspire.com/works/4d101f63c10a6-2', 'c' => 'http://talisaspire.com/works/4d101f63c10a6-2'], @@ -109,7 +104,7 @@ public function testAddTripodArraySingleDoc() * * @param mixed $value */ - public function testAddTripodArrayContainingValidLiteralValues($value) + public function testAddTripodArrayContainingValidLiteralValues($value): void { $doc = [ '_id' => ['r' => 'http://talisaspire.com/works/4d101f63c10a6-2', 'c' => 'http://talisaspire.com/works/4d101f63c10a6-2'], @@ -134,14 +129,12 @@ public function testAddTripodArrayContainingValidLiteralValues($value) $this->assertEquals($expected, $g); } - public function addTripodArrayContainingValidLiteralValues_Provider() + public function addTripodArrayContainingValidLiteralValues_Provider(): iterable { - return [ - ['A String'], - [1], - [1.2], - [true], - ]; + yield ['A String']; + yield [1]; + yield [1.2]; + yield [true]; } /** @@ -149,7 +142,7 @@ public function addTripodArrayContainingValidLiteralValues_Provider() * * @param mixed $value */ - public function testAddTripodArrayContainingInvalidLiteralValues($value) + public function testAddTripodArrayContainingInvalidLiteralValues($value): void { $doc = [ '_id' => ['r' => 'http://talisaspire.com/works/4d101f63c10a6-2', 'c' => 'http://talisaspire.com/works/4d101f63c10a6-2'], @@ -172,13 +165,11 @@ public function testAddTripodArrayContainingInvalidLiteralValues($value) $this->assertEquals($expected, $g); } - public function addTripodArrayContainingInvalidLiteralValues_Provider() + public function addTripodArrayContainingInvalidLiteralValues_Provider(): iterable { - return [ - [null], - [new stdClass()], - [function (): void {}], - ]; + yield [null]; + yield [new stdClass()]; + yield [function (): void {}]; } /** @@ -186,9 +177,9 @@ public function addTripodArrayContainingInvalidLiteralValues_Provider() * * @param mixed $value */ - public function testAddTripodArrayContainingInvalidPredicates($value) + public function testAddTripodArrayContainingInvalidPredicates($value): void { - $this->expectException(LabellerException::class); + $this->expectExceptionMessage('The predicate must be a non-empty string'); $doc = [ '_id' => ['r' => 'http://talisaspire.com/works/4d101f63c10a6-2', 'c' => 'http://talisaspire.com/works/4d101f63c10a6-2'], '_version' => 0, @@ -207,23 +198,21 @@ public function testAddTripodArrayContainingInvalidPredicates($value) $g->add_tripod_array($doc); } - public function addTripodArrayContainingInvalidPredicates_Provider() + public function addTripodArrayContainingInvalidPredicates_Provider(): iterable { - return [ - [1], - [1.2], - [true], - ]; + yield [1]; + yield [1.2]; + yield [true]; } /** * We are expecting the labeller. */ - public function testAddTripodArrayContainingEmptyPredicate() + public function testAddTripodArrayContainingEmptyPredicate(): void { // Should not be able to label '' - $this->expectException(Tripod\Exceptions\Exception::class); - $this->expectExceptionMessage('The predicate cannot be an empty string'); + $this->expectException(Exception::class); + $this->expectExceptionMessage('The predicate must be a non-empty string'); $doc = [ '_id' => ['r' => 'http://talisaspire.com/works/4d101f63c10a6-2', 'c' => 'http://talisaspire.com/works/4d101f63c10a6-2'], '_version' => 0, @@ -247,9 +236,9 @@ public function testAddTripodArrayContainingEmptyPredicate() * * @param mixed $value */ - public function testAddTripodArrayContainingInvalidSubject($value) + public function testAddTripodArrayContainingInvalidSubject($value): void { - $this->expectException(Tripod\Exceptions\Exception::class); + $this->expectException(Exception::class); $doc = [ '_id' => ['r' => $value, 'c' => 'http://talisaspire.com/works/4d101f63c10a6-2'], '_version' => 0, @@ -263,17 +252,15 @@ public function testAddTripodArrayContainingInvalidSubject($value) $g->add_tripod_array($doc); } - public function addTripodArrayContainingInvalidSubject_Provider() + public function addTripodArrayContainingInvalidSubject_Provider(): iterable { - return [ - [''], - [1], - [1.2], - [true], - ]; + yield ['']; + yield [1]; + yield [1.2]; + yield [true]; } - public function testAddTripodArrayContainingValidResourceValues() + public function testAddTripodArrayContainingValidResourceValues(): void { $value = 'A String'; $doc = [ @@ -304,7 +291,7 @@ public function testAddTripodArrayContainingValidResourceValues() * * @param mixed $value */ - public function testAddTripodArrayContainingInvalidResourceValues($value) + public function testAddTripodArrayContainingInvalidResourceValues($value): void { $doc = [ '_id' => ['r' => 'http://talisaspire.com/works/4d101f63c10a6-2', 'c' => 'http://talisaspire.com/works/4d101f63c10a6-2'], @@ -327,20 +314,18 @@ public function testAddTripodArrayContainingInvalidResourceValues($value) $this->assertEquals($expected, $g); } - public function addTripodArrayContainingInvalidResourceValues_Provider() + public function addTripodArrayContainingInvalidResourceValues_Provider(): iterable { - return [ - [1], - [1.2], - [true], - [[]], - [null], - [new stdClass()], - [function (): void {}], - ]; + yield [1]; + yield [1.2]; + yield [true]; + yield [[]]; + yield [null]; + yield [new stdClass()]; + yield [function (): void {}]; } - public function testAddTripodArrayWhenAddingViews() + public function testAddTripodArrayWhenAddingViews(): void { // view contains 4 subgraphs $view = json_decode(file_get_contents(__DIR__ . '/data/view.json'), true); @@ -348,7 +333,7 @@ public function testAddTripodArrayWhenAddingViews() $g->add_tripod_array($view); // graph should contain 4 subgraphs - $this->assertEquals(4, count($g->get_subjects())); + $this->assertCount(4, $g->get_subjects()); // assert each subgraph $this->assertHasLiteralTriple($g, 'http://example.com/resources/1', $g->qname_to_uri('dct:date'), '2003'); @@ -370,7 +355,7 @@ public function testAddTripodArrayWhenAddingViews() $this->assertHasResourceTriple($g, 'http://example.com/resources/1/authors', $g->qname_to_uri('rdf:type'), 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq'); } - public function testToTripodArray() + public function testToTripodArray(): void { $expected = [ '_id' => ['r' => 'http://talisaspire.com/works/4d101f63c10a6-2', 'c' => 'http://example.com/'], @@ -394,14 +379,14 @@ public function testToTripodArray() $this->assertEquals($expected, $actual); } - public function testToTripodArrayReturnsNullIfDocNotInGraph() + public function testToTripodArrayReturnsNullIfDocNotInGraph(): void { $g = new MongoGraph(); $doc = $g->to_tripod_array('http://example.com/1', 'http://example.com/'); $this->assertNull($doc); } - public function testToTripodViewArray() + public function testToTripodViewArray(): void { $expected = [ '_id' => ['r' => 'http://example.com/things/1', 'c' => 'http://example.com/'], @@ -432,10 +417,10 @@ public function testToTripodViewArray() $g->add_resource_triple('http://example.com/things/2', $g->qname_to_uri('rdf:type'), 'http://talisaspire.com/schema#Work'); $actual = $g->to_tripod_view_array('http://example.com/things/1', 'http://example.com/'); - $this->assertEquals($expected, $actual); + $this->assertSame($expected, $actual); } - public function testWriteLockedDocDoesNotExposeVersionOrLockPropertyInGraph() + public function testWriteLockedDocDoesNotExposeVersionOrLockPropertyInGraph(): void { $doc = [ '_id' => ['r' => 'http://example.com/things/1', 'c' => 'http://example.com/'], @@ -445,6 +430,6 @@ public function testWriteLockedDocDoesNotExposeVersionOrLockPropertyInGraph() $g = new MongoGraph(); $g->add_tripod_array($doc); - $this->assertTrue(count($g->get_index()) == 0, 'Graph should contain no data'); + $this->assertCount(0, $g->get_index(), 'Graph should contain no data'); } } diff --git a/test/unit/mongo/MongoSearchProviderTest.php b/test/unit/mongo/MongoSearchProviderTest.php index 92a68d89..f0560e62 100644 --- a/test/unit/mongo/MongoSearchProviderTest.php +++ b/test/unit/mongo/MongoSearchProviderTest.php @@ -1,5 +1,7 @@ tripodTransactionLog->purgeAllTransactions(); $this->tripod = new Driver('CBD_testing', 'tripod_php_testing'); - $this->indexer = new SearchIndexer($this->tripod); - $this->searchProvider = new MongoSearchProvider($this->tripod); + $this->searchIndexer = new SearchIndexer($this->tripod); + $this->mongoSearchProvider = new MongoSearchProvider($this->tripod); $this->getTripodCollection($this->tripod)->drop(); $this->loadBaseSearchDataViaTripod(); @@ -52,11 +52,12 @@ protected function setUp(): void } } } - $this->indexer->generateAndIndexSearchDocuments($result['_id']['r'], $result['_id']['c'], $this->tripod->getPodName()); + + $this->searchIndexer->generateAndIndexSearchDocuments($result['_id']['r'], $result['_id']['c'], $this->tripod->getPodName()); } } - public function testSearchIndexing() + public function testSearchIndexing(): void { // assert that there are only 12 based on the data we loaded into tripod $actualSearchDocumentCount = $this->getCountForSearchSpecs($this->tripod); @@ -185,7 +186,7 @@ public function testSearchIndexing() } } - public function testSearchIndexingRemovesDocWhenTypeHasNoCorrespondingSearchdocSpec() + public function testSearchIndexingRemovesDocWhenTypeHasNoCorrespondingSearchdocSpec(): void { // update a document $id = ['_id.r' => 'http://talisaspire.com/resources/doc1']; @@ -193,7 +194,7 @@ public function testSearchIndexingRemovesDocWhenTypeHasNoCorrespondingSearchdocS ->updateOne($id, ['$set' => ['rdf:type' => ['u' => 'bibo:Article']]]); // reindex - $this->indexer->generateAndIndexSearchDocuments('http://talisaspire.com/resources/doc1', 'http://talisaspire.com/', $this->tripod->getPodName()); + $this->searchIndexer->generateAndIndexSearchDocuments('http://talisaspire.com/resources/doc1', 'http://talisaspire.com/', $this->tripod->getPodName()); $actualSearchDocumentCount = $this->getCountForSearchSpecs($this->tripod); @@ -207,7 +208,7 @@ public function testSearchIndexingRemovesDocWhenTypeHasNoCorrespondingSearchdocS } } - public function testSearchIndexingGeneratesNewDocForChangedTypeThatHasACorrespondingSearchdocSpec() + public function testSearchIndexingGeneratesNewDocForChangedTypeThatHasACorrespondingSearchdocSpec(): void { // update a document $id = ['_id.r' => 'http://talisaspire.com/resources/doc1']; @@ -220,7 +221,7 @@ public function testSearchIndexingGeneratesNewDocForChangedTypeThatHasACorrespon $this->getTripodCollection($this->tripod)->updateOne($id, ['$set' => $newData]); // reindex - $this->indexer->generateAndIndexSearchDocuments('http://talisaspire.com/resources/doc1', 'http://talisaspire.com/', $this->tripod->getPodName()); + $this->searchIndexer->generateAndIndexSearchDocuments('http://talisaspire.com/resources/doc1', 'http://talisaspire.com/', $this->tripod->getPodName()); $actualSearchDocumentCount = $this->getCountForSearchSpecs($this->tripod); @@ -233,6 +234,7 @@ public function testSearchIndexingGeneratesNewDocForChangedTypeThatHasACorrespon break; } } + $this->assertEquals($result['_id'], [ 'r' => 'http://talisaspire.com/resources/doc1', 'c' => 'http://talisaspire.com/', @@ -240,7 +242,7 @@ public function testSearchIndexingGeneratesNewDocForChangedTypeThatHasACorrespon ]); } - public function testSearchIndexingGeneratesTwoDocumentsForGivenResourceTheDeletesOneAfterFurtherUpdate() + public function testSearchIndexingGeneratesTwoDocumentsForGivenResourceTheDeletesOneAfterFurtherUpdate(): void { // update a document $id = ['_id.r' => 'http://talisaspire.com/resources/doc1']; @@ -253,7 +255,7 @@ public function testSearchIndexingGeneratesTwoDocumentsForGivenResourceTheDelete $this->getTripodCollection($this->tripod)->updateOne($id, ['$set' => $newData]); // reindex - $this->indexer->generateAndIndexSearchDocuments('http://talisaspire.com/resources/doc1', 'http://talisaspire.com/', $this->tripod->getPodName()); + $this->searchIndexer->generateAndIndexSearchDocuments('http://talisaspire.com/resources/doc1', 'http://talisaspire.com/', $this->tripod->getPodName()); $actualSearchDocumentCount = $this->getCountForSearchSpecs($this->tripod); @@ -269,7 +271,7 @@ public function testSearchIndexingGeneratesTwoDocumentsForGivenResourceTheDelete } } - $this->assertEquals(2, count($results)); + $this->assertCount(2, $results); $expected = [ [ 'r' => 'http://talisaspire.com/resources/doc1', @@ -283,10 +285,7 @@ public function testSearchIndexingGeneratesTwoDocumentsForGivenResourceTheDelete ], ]; foreach ($results as $result) { - $this->assertTrue(in_array( - $result['_id'], - $expected - )); + $this->assertContains($result['_id'], $expected); } // now update it again removing the resourcelist:List type @@ -296,7 +295,7 @@ public function testSearchIndexingGeneratesTwoDocumentsForGivenResourceTheDelete $this->getTripodCollection($this->tripod)->updateOne($id, ['$set' => $newData]); // reindex - $this->indexer->generateAndIndexSearchDocuments('http://talisaspire.com/resources/doc1', 'http://talisaspire.com/', $this->tripod->getPodName()); + $this->searchIndexer->generateAndIndexSearchDocuments('http://talisaspire.com/resources/doc1', 'http://talisaspire.com/', $this->tripod->getPodName()); $actualSearchDocumentCount = $this->getCountForSearchSpecs($this->tripod); @@ -312,7 +311,7 @@ public function testSearchIndexingGeneratesTwoDocumentsForGivenResourceTheDelete } } - $this->assertEquals(1, count($results)); + $this->assertCount(1, $results); $result = array_pop($results); $this->assertEquals($result['_id'], [ @@ -322,51 +321,51 @@ public function testSearchIndexingGeneratesTwoDocumentsForGivenResourceTheDelete ]); } - public function testSearchThrowsExceptionIfNoQuery() + public function testSearchThrowsExceptionIfNoQuery(): void { $this->expectException(SearchException::class); $this->expectExceptionMessage('You must specify a query'); - $this->searchProvider->search('', 'i_search_resource', ['search_terms'], ['result'], 3, 0); + $this->mongoSearchProvider->search('', 'i_search_resource', ['search_terms'], ['result'], 3, 0); } - public function testSearchThrowsExceptionIfNoType() + public function testSearchThrowsExceptionIfNoType(): void { $this->expectException(SearchException::class); $this->expectExceptionMessage('You must specify the search document type to restrict the query to'); - $this->searchProvider->search('poetry', '', ['search_terms'], ['result'], 3, 0); + $this->mongoSearchProvider->search('poetry', '', ['search_terms'], ['result'], 3, 0); } - public function testSearchThrowsExceptionIfSearchIndicesEmpty() + public function testSearchThrowsExceptionIfSearchIndicesEmpty(): void { $this->expectException(SearchException::class); $this->expectExceptionMessage('You must specify at least one index from the search document specification to query against'); - $this->searchProvider->search('poetry', 'i_search_resource', [], ['result'], 3, 0); + $this->mongoSearchProvider->search('poetry', 'i_search_resource', [], ['result'], 3, 0); } - public function testSearchThrowsExceptionIfFieldsToReturnEmpty() + public function testSearchThrowsExceptionIfFieldsToReturnEmpty(): void { $this->expectException(SearchException::class); $this->expectExceptionMessage('You must specify at least one field from the search document specification to return'); - $this->searchProvider->search('poetry', 'i_search_resource', ['search_terms'], [], 3, 0); + $this->mongoSearchProvider->search('poetry', 'i_search_resource', ['search_terms'], [], 3, 0); } - public function testSearchThrowsExceptionIfLimitIsNegative() + public function testSearchThrowsExceptionIfLimitIsNegative(): void { $this->expectException(SearchException::class); $this->expectExceptionMessage('Value for limit must be a positive number'); - $this->searchProvider->search('poetry', 'i_search_resource', ['search_terms'], ['result'], -3, 0); + $this->mongoSearchProvider->search('poetry', 'i_search_resource', ['search_terms'], ['result'], -3, 0); } - public function testSearchThrowsExceptionIfOffsetIsNegative() + public function testSearchThrowsExceptionIfOffsetIsNegative(): void { $this->expectException(SearchException::class); $this->expectExceptionMessage('Value for offset must be a positive number'); - $this->searchProvider->search('poetry', 'i_search_resource', ['search_terms'], ['result'], 3, -1); + $this->mongoSearchProvider->search('poetry', 'i_search_resource', ['search_terms'], ['result'], 3, -1); } - public function testSearchLimitAndOffset() + public function testSearchLimitAndOffset(): void { - $results = $this->searchProvider->search('poetry', 'i_search_resource', ['search_terms'], ['result'], 3, 0); + $results = $this->mongoSearchProvider->search('poetry', 'i_search_resource', ['search_terms'], ['result'], 3, 0); $this->assertEquals(3, $results['head']['limit']); $this->assertEquals(0, $results['head']['offset']); @@ -374,23 +373,23 @@ public function testSearchLimitAndOffset() $secondResult = $results['results'][1]; $thirdResult = $results['results'][2]; - $results2 = $this->searchProvider->search('poetry', 'i_search_resource', ['search_terms'], ['result'], 3, 1); + $results2 = $this->mongoSearchProvider->search('poetry', 'i_search_resource', ['search_terms'], ['result'], 3, 1); $this->assertEquals(9, $results2['head']['count']); $this->assertEquals(3, $results2['head']['limit']); $this->assertEquals(1, $results2['head']['offset']); - $this->assertFalse(in_array($firstResult, $results2['results'])); + $this->assertNotContains($firstResult, $results2['results']); $this->assertEquals($secondResult, $results2['results'][0]); $this->assertEquals($thirdResult, $results2['results'][1]); } - public function testSearchSingleIndex() + public function testSearchSingleIndex(): void { // simple search - $results = $this->searchProvider->search('john locke poetry', 'i_search_resource', ['search_terms'], ['result'], 4, 0); + $results = $this->mongoSearchProvider->search('john locke poetry', 'i_search_resource', ['search_terms'], ['result'], 4, 0); $this->assertEquals(6, $results['head']['count']); $this->assertEquals(4, $results['head']['limit']); - $this->assertEquals(4, count($results['results'])); + $this->assertCount(4, $results['results']); $this->assertEquals(0, $results['head']['offset']); $this->assertEquals('john locke poetry', $results['head']['query']); $this->assertEquals(['john', 'locke', 'poetry'], $results['head']['query_terms_used']); @@ -406,20 +405,20 @@ public function testSearchSingleIndex() $this->assertEquals($expectedResults, $results['results']); // search with some stop words - $results = $this->searchProvider->search('the owl and the pussycat', 'i_search_resource', ['search_terms'], ['result'], 3, 0); + $results = $this->mongoSearchProvider->search('the owl and the pussycat', 'i_search_resource', ['search_terms'], ['result'], 3, 0); $this->assertEquals(1, $results['head']['count']); $this->assertEquals(3, $results['head']['limit']); - $this->assertEquals(1, count($results['results'])); + $this->assertCount(1, $results['results']); $this->assertEquals(0, $results['head']['offset']); $this->assertEquals('the owl and the pussycat', $results['head']['query']); $this->assertEquals(['owl', 'pussycat'], $results['head']['query_terms_used']); $this->assertArrayHasKey('duration', $results['head']); // search returns no results - $results = $this->searchProvider->search('october', 'i_search_resource', ['search_terms'], ['result'], 3, 0); + $results = $this->mongoSearchProvider->search('october', 'i_search_resource', ['search_terms'], ['result'], 3, 0); $this->assertEquals(0, $results['head']['count']); $this->assertEquals(3, $results['head']['limit']); - $this->assertEquals(0, count($results['results'])); + $this->assertCount(0, $results['results']); $this->assertEquals(0, $results['head']['offset']); $this->assertEquals('october', $results['head']['query']); $this->assertEquals(['october'], $results['head']['query_terms_used']); @@ -427,10 +426,10 @@ public function testSearchSingleIndex() $this->assertEquals([], $results['results']); // search single index but return multiple fields - $results = $this->searchProvider->search('john locke poetry', 'i_search_resource', ['search_terms'], ['result', 'rdftype'], 3, 0); + $results = $this->mongoSearchProvider->search('john locke poetry', 'i_search_resource', ['search_terms'], ['result', 'rdftype'], 3, 0); $this->assertEquals(6, $results['head']['count']); $this->assertEquals(3, $results['head']['limit']); - $this->assertEquals(3, count($results['results'])); + $this->assertCount(3, $results['results']); $this->assertEquals(0, $results['head']['offset']); $this->assertEquals('john locke poetry', $results['head']['query']); $this->assertEquals(['john', 'locke', 'poetry'], $results['head']['query_terms_used']); @@ -444,12 +443,12 @@ public function testSearchSingleIndex() $this->assertEquals($expectedResults, $results['results']); } - public function testSearchMultipleIndices() + public function testSearchMultipleIndices(): void { - $results = $this->searchProvider->search('bibo:Book', 'i_search_resource', ['search_terms', 'other_terms'], ['result', 'rdftype'], 3, 0); + $results = $this->mongoSearchProvider->search('bibo:Book', 'i_search_resource', ['search_terms', 'other_terms'], ['result', 'rdftype'], 3, 0); $this->assertEquals(13, $results['head']['count']); $this->assertEquals(3, $results['head']['limit']); - $this->assertEquals(3, count($results['results'])); + $this->assertCount(3, $results['results']); $this->assertEquals(0, $results['head']['offset']); $this->assertEquals('bibo:Book', $results['head']['query']); $this->assertEquals(['bibo:book'], $results['head']['query_terms_used']); @@ -464,7 +463,7 @@ public function testSearchMultipleIndices() $this->assertEquals($expectedResults, $results['results']); } - public function testSearchWorksDirectlyFromTripod() + public function testSearchWorksDirectlyFromTripod(): void { $results = $this->tripod->search([ 'q' => 'john locke poetry', @@ -477,7 +476,7 @@ public function testSearchWorksDirectlyFromTripod() $this->assertEquals(6, $results['head']['count']); $this->assertEquals(3, $results['head']['limit']); - $this->assertEquals(3, count($results['results'])); + $this->assertCount(3, $results['results']); $this->assertEquals(0, $results['head']['offset']); $this->assertEquals('john locke poetry', $results['head']['query']); $this->assertEquals(['john', 'locke', 'poetry'], $results['head']['query_terms_used']); @@ -492,7 +491,7 @@ public function testSearchWorksDirectlyFromTripod() $this->assertEquals($expectedResults, $results['results']); } - public function testDeleteSearchDocumentsByTypeIdThrowsExceptionForInvalidType() + public function testDeleteSearchDocumentsByTypeIdThrowsExceptionForInvalidType(): void { $mockSearchProvider = $this->getMockBuilder(MongoSearchProvider::class) ->onlyMethods(['getSearchDocumentSpecification']) @@ -501,14 +500,14 @@ public function testDeleteSearchDocumentsByTypeIdThrowsExceptionForInvalidType() $mockSearchProvider->expects($this->once()) ->method('getSearchDocumentSpecification') ->with('i_some_type') - ->will($this->returnValue(null)); + ->willReturn(null); $this->expectException(Exception::class); $this->expectExceptionMessage('Could not find a search specification for i_some_type'); $mockSearchProvider->deleteSearchDocumentsByTypeId('i_some_type'); } - public function testDeleteSearchDocumentsByTypeIdDeletesNothingWhenNoMatchFound() + public function testDeleteSearchDocumentsByTypeIdDeletesNothingWhenNoMatchFound(): void { // first, assert that there are only 12 based on the data we loaded into tripod $actualSearchDocumentCount = $this->getCountForSearchSpecs($this->tripod); @@ -522,12 +521,12 @@ public function testDeleteSearchDocumentsByTypeIdDeletesNothingWhenNoMatchFound( $mockSearchProvider->expects($this->once()) ->method('getSearchDocumentSpecification') ->with('i_some_type') - ->will($this->returnValue(['i_some_type' => []])); + ->willReturn(['i_some_type' => []]); try { $mockSearchProvider->deleteSearchDocumentsByTypeId('i_some_type'); } catch (ConfigException $e) { - $this->assertEquals("Search document id 'i_some_type' not in configuration for store 'tripod_php_testing'", $e->getMessage()); + $this->assertSame("Search document id 'i_some_type' not in configuration for store 'tripod_php_testing'", $e->getMessage()); } // search document count should remain same, because we expect that there was nothing to delete @@ -536,7 +535,7 @@ public function testDeleteSearchDocumentsByTypeIdDeletesNothingWhenNoMatchFound( $this->assertEquals(13, $newSearchDocumentCount, 'Should have generated 12 search documents, because there was no match to remove'); } - public function testDeleteSearchDocumentsByTypeIdDeleteAllMatchingDocuments() + public function testDeleteSearchDocumentsByTypeIdDeleteAllMatchingDocuments(): void { // first, assert that there are only 12 based on the data we loaded into tripod $actualSearchDocumentCount = $this->getCountForSearchSpecs($this->tripod); @@ -550,7 +549,7 @@ public function testDeleteSearchDocumentsByTypeIdDeleteAllMatchingDocuments() $mockSearchProvider->expects($this->once()) ->method('getSearchDocumentSpecification') ->with('i_search_resource') - ->will($this->returnValue(['i_search_resource' => []])); + ->willReturn(['i_search_resource' => []]); $mockSearchProvider->deleteSearchDocumentsByTypeId('i_search_resource'); @@ -560,7 +559,7 @@ public function testDeleteSearchDocumentsByTypeIdDeleteAllMatchingDocuments() $this->assertEquals(0, $newSearchDocumentCount, 'Should have 0 search documents after removing all matching documents'); } - public function testDeleteSearchDocumentsByTypeIdDoNotDeleteNonMatchingDocuments() + public function testDeleteSearchDocumentsByTypeIdDoNotDeleteNonMatchingDocuments(): void { // first, assert that there are only 12 based on the data we loaded into tripod $actualSearchDocumentCount = $this->getCountForSearchSpecs($this->tripod); @@ -577,7 +576,7 @@ public function testDeleteSearchDocumentsByTypeIdDoNotDeleteNonMatchingDocuments $this->getTripodCollection($this->tripod)->updateOne($id, ['$set' => $newData]); // reindex - $this->indexer->generateAndIndexSearchDocuments('http://talisaspire.com/resources/doc1', 'http://talisaspire.com/', $this->tripod->getPodName()); + $this->searchIndexer->generateAndIndexSearchDocuments('http://talisaspire.com/resources/doc1', 'http://talisaspire.com/', $this->tripod->getPodName()); // assert that there are now 13 documents after adding new document to collection $updatedSearchDocumentCount = $this->getCountForSearchSpecs($this->tripod); @@ -591,7 +590,7 @@ public function testDeleteSearchDocumentsByTypeIdDoNotDeleteNonMatchingDocuments $mockSearchProvider->expects($this->once()) ->method('getSearchDocumentSpecification') ->with('i_search_resource') - ->will($this->returnValue(['i_search_resource' => []])); + ->willReturn(['i_search_resource' => []]); $mockSearchProvider->deleteSearchDocumentsByTypeId('i_search_resource'); @@ -601,7 +600,7 @@ public function testDeleteSearchDocumentsByTypeIdDoNotDeleteNonMatchingDocuments $this->assertEquals(1, $newSearchDocumentCount, "Should have 1 search documents since there is one search document with 'i_search_list' type that does not match delete type."); } - public function testCountSearchDocuments() + public function testCountSearchDocuments(): void { $tripod = $this->getMockBuilder(Driver::class) ->setConstructorArgs(['CBD_testing', 'tripod_php_testing']) @@ -619,23 +618,23 @@ public function testCountSearchDocuments() $search->expects($this->once()) ->method('getCollectionForSearchSpec') ->with('i_search_list') - ->will($this->returnValue($collection)); + ->willReturn($collection); $collection->expects($this->once()) ->method('count') ->with(['_id.type' => 'i_search_list']) - ->will($this->returnValue(21)); + ->willReturn(21); $this->assertEquals(21, $search->count('i_search_list')); } - public function testCountSearchDocumentsWithFilters() + public function testCountSearchDocumentsWithFilters(): void { $tripod = $this->getMockBuilder(Driver::class) ->setConstructorArgs(['CBD_testing', 'tripod_php_testing']) ->getMock(); - $filters = ['_cts' => ['$lte' => new UTCDateTime(null)]]; + $filters = ['_cts' => ['$lte' => new UTCDateTime()]]; $query = array_merge(['_id.type' => 'i_search_list'], $filters); $collection = $this->getMockBuilder(Collection::class) ->setConstructorArgs([new Manager(), 'db', 'coll']) @@ -649,17 +648,17 @@ public function testCountSearchDocumentsWithFilters() $search->expects($this->once()) ->method('getCollectionForSearchSpec') ->with('i_search_list') - ->will($this->returnValue($collection)); + ->willReturn($collection); $collection->expects($this->once()) ->method('count') ->with($query) - ->will($this->returnValue(89)); + ->willReturn(89); $this->assertEquals(89, $search->count('i_search_list', $filters)); } - public function testDeleteSearchDocumentsBySearchId() + public function testDeleteSearchDocumentsBySearchId(): void { $tripod = $this->getMockBuilder(Driver::class) ->setConstructorArgs(['CBD_testing', 'tripod_php_testing']) @@ -677,7 +676,7 @@ public function testDeleteSearchDocumentsBySearchId() $deleteResult->expects($this->once()) ->method('getDeletedCount') - ->will($this->returnValue(9)); + ->willReturn(9); $search = $this->getMockBuilder(MongoSearchProvider::class) ->onlyMethods(['getCollectionForSearchSpec', 'getSearchDocumentSpecification']) @@ -687,24 +686,24 @@ public function testDeleteSearchDocumentsBySearchId() $search->expects($this->once()) ->method('getSearchDocumentSpecification') ->with('i_search_list') - ->will($this->returnValue(['_id' => 'i_search_list'])); + ->willReturn(['_id' => 'i_search_list']); $search->expects($this->once()) ->method('getCollectionForSearchSpec') ->with('i_search_list') - ->will($this->returnValue($collection)); + ->willReturn($collection); $collection->expects($this->once()) ->method('deleteMany') ->with(['_id.type' => 'i_search_list']) - ->will($this->returnValue($deleteResult)); + ->willReturn($deleteResult); $this->assertEquals(9, $search->deleteSearchDocumentsByTypeId('i_search_list')); } - public function testDeleteSearchDocumentsBySearchIdWithTimestamp() + public function testDeleteSearchDocumentsBySearchIdWithTimestamp(): void { - $timestamp = new UTCDateTime(null); + $timestamp = new UTCDateTime(); $query = [ '_id.type' => 'i_search_list', @@ -730,7 +729,7 @@ public function testDeleteSearchDocumentsBySearchIdWithTimestamp() $deleteResult->expects($this->once()) ->method('getDeletedCount') - ->will($this->returnValue(9)); + ->willReturn(9); $search = $this->getMockBuilder(MongoSearchProvider::class) ->onlyMethods(['getCollectionForSearchSpec', 'getSearchDocumentSpecification']) @@ -740,17 +739,17 @@ public function testDeleteSearchDocumentsBySearchIdWithTimestamp() $search->expects($this->once()) ->method('getSearchDocumentSpecification') ->with('i_search_list') - ->will($this->returnValue(['_id' => 'i_search_list'])); + ->willReturn(['_id' => 'i_search_list']); $search->expects($this->once()) ->method('getCollectionForSearchSpec') ->with('i_search_list') - ->will($this->returnValue($collection)); + ->willReturn($collection); $collection->expects($this->once()) ->method('deleteMany') ->with($query) - ->will($this->returnValue($deleteResult)); + ->willReturn($deleteResult); $this->assertEquals(9, $search->deleteSearchDocumentsByTypeId('i_search_list', $timestamp)); } diff --git a/test/unit/mongo/MongoTransactionLogTest.php b/test/unit/mongo/MongoTransactionLogTest.php index 66348164..32b3a747 100644 --- a/test/unit/mongo/MongoTransactionLogTest.php +++ b/test/unit/mongo/MongoTransactionLogTest.php @@ -1,5 +1,7 @@ tripodTransactionLog = new TransactionLog(); $this->tripodTransactionLog->purgeAllTransactions(); + $this->tripod->setTransactionLog($this->tripodTransactionLog); } - public function testReplayLogSimpleChangesSinglePropertyOnExistingDocument() + public function testReplayLogSimpleChangesSinglePropertyOnExistingDocument(): void { $uri = 'http://talisaspire.com/examples/1'; @@ -52,6 +55,7 @@ public function testReplayLogSimpleChangesSinglePropertyOnExistingDocument() $originalGraph->add_resource_triple($uri, $originalGraph->qname_to_uri('rdf:type'), $originalGraph->qname_to_uri('acorn:Resource')); $originalGraph->add_literal_triple($uri, $originalGraph->qname_to_uri('searchterms:title'), 'Physics 3rd Edition'); $originalGraph->add_literal_triple($uri, $originalGraph->qname_to_uri('searchterms:author'), 'Joe Bloggs'); + $this->tripod->saveChanges(new ExtendedGraph(), $originalGraph, 'http://talisaspire.com/'); // jsut confirm the values we just added were set in the store $oG = $this->tripod->describeResource($uri); @@ -94,13 +98,14 @@ public function testReplayLogSimpleChangesSinglePropertyOnExistingDocument() $this->assertDocumentVersion(['r' => $uri, 'c' => 'http://talisaspire.com/'], 1); } - public function testReplayLogSimpleAddSingleNewDocument() + public function testReplayLogSimpleAddSingleNewDocument(): void { $uri = 'http://example.com/resources/1'; $g = new MongoGraph(); $g->add_resource_triple($uri, $g->qname_to_uri('rdf:type'), $g->qname_to_uri('acorn:Resource')); $g->add_literal_triple($uri, $g->qname_to_uri('dct:title'), 'wibble'); + $this->tripod->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/', 'something new'); // make sure the new entity was saved correctly @@ -124,13 +129,14 @@ public function testReplayLogSimpleAddSingleNewDocument() $this->assertDocumentVersion(['r' => $uri, 'c' => 'http://talisaspire.com/'], 0); } - public function testReplayLogSimpleAddSingleNewDocumentNamespacedResourceAndContext() + public function testReplayLogSimpleAddSingleNewDocumentNamespacedResourceAndContext(): void { $uri = 'http://basedata.com/b/3'; $g = new MongoGraph(); $g->add_resource_triple($uri, $g->qname_to_uri('rdf:type'), $g->qname_to_uri('acorn:Resource')); $g->add_literal_triple($uri, $g->qname_to_uri('dct:title'), 'wibble'); + $this->tripod->saveChanges(new MongoGraph(), $g, 'http://basedata.com/b/DefaultGraph', 'something new'); // make sure the new entity was saved correctly @@ -160,7 +166,7 @@ public function testReplayLogSimpleAddSingleNewDocumentNamespacedResourceAndCont * We then drop the collection and re-add the original document. * We replay the transaction log to ensure that it does physically remove the document. */ - public function testReplayLogSimpleDeletesSingleEntity() + public function testReplayLogSimpleDeletesSingleEntity(): void { // document is added via setup() method $uri = 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA'; @@ -194,7 +200,7 @@ public function testReplayLogSimpleDeletesSingleEntity() * Since we step backwards through the transaction log. This test verifies that if a more recent * transaction deleted an entity then an older transaction should not re-introduce it. */ - public function testReplayLogSimpleDeletesSingleEntityWithNoBaseData() + public function testReplayLogSimpleDeletesSingleEntityWithNoBaseData(): void { // document is added via setup() method $uri = 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA'; @@ -202,8 +208,10 @@ public function testReplayLogSimpleDeletesSingleEntityWithNoBaseData() // Save a change to entity $oG = new MongoGraph(); $oG->add_literal_triple($uri, $oG->qname_to_uri('searchterms:title'), 'Physics 3rd Edition'); + $nG = new MongoGraph(); $nG->add_literal_triple($uri, $oG->qname_to_uri('searchterms:title'), 'A different title'); + $this->tripod->saveChanges($oG, $nG, 'http://talisaspire.com/'); $this->assertDocumentVersion(['r' => $uri, 'c' => 'http://talisaspire.com/'], 1); @@ -225,7 +233,7 @@ public function testReplayLogSimpleDeletesSingleEntityWithNoBaseData() $this->assertDocumentHasBeenDeleted(['r' => $uri, 'c' => 'http://talisaspire.com/']); } - public function testReplayTransactionsOverSeveralDocumentsAddingAndUpdating() + public function testReplayTransactionsOverSeveralDocumentsAddingAndUpdating(): void { // base documents added via setup() method $uri_1 = 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA'; @@ -236,6 +244,7 @@ public function testReplayTransactionsOverSeveralDocumentsAddingAndUpdating() $oG->add_literal_triple($uri_1, $oG->qname_to_uri('searchterms:title'), 'Physics 3rd Edition'); $oG->add_literal_triple($uri_2, $oG->qname_to_uri('searchterms:discipline'), 'physics'); $oG->add_resource_triple($uri_2, $oG->qname_to_uri('dct:subject'), 'http://talisaspire.com/disciplines/physics'); + $nG = new MongoGraph(); $nG->add_literal_triple($uri_1, $nG->qname_to_uri('searchterms:title'), 'History of UK'); $nG->add_literal_triple($uri_2, $nG->qname_to_uri('searchterms:discipline'), 'history'); @@ -258,6 +267,7 @@ public function testReplayTransactionsOverSeveralDocumentsAddingAndUpdating() // change same entity again: $nG2 = new MongoGraph(); $nG2->add_literal_triple($uri_2, $nG2->qname_to_uri('searchterms:title'), 'some test title'); + $nG3 = new MongoGraph(); $nG3->add_literal_triple($uri_2, $nG2->qname_to_uri('searchterms:title'), 'a different title'); @@ -271,6 +281,7 @@ public function testReplayTransactionsOverSeveralDocumentsAddingAndUpdating() // change same entity again: $nG2 = new MongoGraph(); $nG2->add_literal_triple($uri_1, $nG2->qname_to_uri('searchterms:title'), 'History of UK'); + $nG3 = new MongoGraph(); $nG3->add_literal_triple($uri_1, $nG2->qname_to_uri('searchterms:title'), 'History of the United Kingdom'); @@ -298,7 +309,7 @@ public function testReplayTransactionsOverSeveralDocumentsAddingAndUpdating() $this->assertHasResourceTriple($resource2, $uri_2, $resource1->qname_to_uri('dct:subject'), 'http://talisaspire.com/disciplines/history'); } - public function testReplayTransactionsAddingAndDeleting() + public function testReplayTransactionsAddingAndDeleting(): void { $uri = 'http://example.com/resources/1'; $g = new MongoGraph(); @@ -337,7 +348,7 @@ public function testReplayTransactionsAddingAndDeleting() * This test addes a set of precanned transactions to the transaction log, and then verifies that * only the transactions $gte the given date are replayed. */ - public function testReplayTransactionsFromAGivenDate() + public function testReplayTransactionsFromAGivenDate(): void { $transaction_1 = $this->buildTransactionDocument(1, 'http://example.com/resources/1', '2013-01-21T13:00:00.000Z', '2013-01-21T13:01:00.000Z', 0); $transaction_2 = $this->buildTransactionDocument(2, 'http://example.com/resources/2', '2013-01-21T13:00:00.000Z', '2013-01-21T13:02:00.000Z', 0); @@ -373,7 +384,7 @@ public function testReplayTransactionsFromAGivenDate() * This test addes a set of precanned transactions to the transaction log, and then verifies that * only the transactions $gte the given date are replayed. */ - public function testReplayTransactionsBetweenTwoDates() + public function testReplayTransactionsBetweenTwoDates(): void { $transaction_1 = $this->buildTransactionDocument(1, 'http://example.com/resources/1', '2013-01-21T13:00:00.000Z', '2013-01-21T13:01:00.000Z', 0); $transaction_2 = $this->buildTransactionDocument(2, 'http://example.com/resources/2', '2013-01-21T13:00:00.000Z', '2013-01-21T13:02:00.000Z', 0); @@ -405,7 +416,7 @@ public function testReplayTransactionsBetweenTwoDates() $this->assertFalse($g->has_triples_about('http://example.com/resources/5'), 'Should not contain triples about /resources/5'); } - public function testTransactionIsLoggedCorrectlyWhenCompletedSuccessfully() + public function testTransactionIsLoggedCorrectlyWhenCompletedSuccessfully(): void { // STEP 1 $uri = 'http://example.com/resources/1'; @@ -425,10 +436,10 @@ public function testTransactionIsLoggedCorrectlyWhenCompletedSuccessfully() $mTripodUpdate->expects($this->atLeastOnce()) ->method('getUniqId') - ->will($this->returnValue(1)); + ->willReturn('1'); $mTripod->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($mTripodUpdate)); + ->willReturn($mTripodUpdate); $mTripod->setTransactionLog($this->tripodTransactionLog); $mTripod->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/'); @@ -438,18 +449,14 @@ public function testTransactionIsLoggedCorrectlyWhenCompletedSuccessfully() $transactionDocument = $this->getDocument($transactionId, $this->tripod, true); $this->assertEquals($transactionId, $transactionDocument['_id'], 'transtion should have the mocked id we injected'); $this->assertEquals('completed', $transactionDocument['status'], 'status of the transaction should be completed'); - $this->assertEquals(1, count($transactionDocument['originalCBDs']), 'There should only be on CBD in the originalCBs collection'); + $this->assertCount(1, $transactionDocument['originalCBDs'], 'There should only be on CBD in the originalCBs collection'); $this->assertTransactionDate($transactionDocument, 'startTime'); $this->assertTransactionDate($transactionDocument, 'endTime'); - $this->assertTrue(isset($transactionDocument['changes']), 'Transaction should contain changes'); + $this->assertArrayHasKey('changes', $transactionDocument, 'Transaction should contain changes'); $this->assertChangesForGivenSubject($transactionDocument['changes'], $uri, 2, 0); $expectedCBD = ['_id' => ['r' => 'http://example.com/resources/1', 'c' => 'http://talisaspire.com/']]; $actualCBD = $transactionDocument['originalCBDs'][0]; $this->assertEquals($expectedCBD, $actualCBD, 'CBD in transaction should match our expected value exactly'); - - // STEP 2 - // update the same entity with an addition - $mTripod = null; $mTripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getDataUpdater']) ->setConstructorArgs(['CBD_testing', 'tripod_php_testing']) @@ -461,25 +468,26 @@ public function testTransactionIsLoggedCorrectlyWhenCompletedSuccessfully() $mTripodUpdate->expects($this->atLeastOnce()) ->method('getUniqId') - ->will($this->returnValue(2)); + ->willReturn('2'); $mTripod->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($mTripodUpdate)); + ->willReturn($mTripodUpdate); $mTripod->setTransactionLog($this->tripodTransactionLog); $nG = new MongoGraph(); $nG->add_graph($g); $nG->add_literal_triple($uri, $g->qname_to_uri('dct:title'), 'another title'); + $mTripod->saveChanges($g, $nG, 'http://talisaspire.com/'); // assert this transaction is correct $transactionId = 'transaction_2'; $transactionDocument = $this->getDocument($transactionId, $this->tripod, true); $this->assertEquals($transactionId, $transactionDocument['_id'], 'transtion should have the mocked id we injected'); $this->assertEquals('completed', $transactionDocument['status'], 'status of the transaction should be completed'); - $this->assertEquals(1, count($transactionDocument['originalCBDs']), 'There should only be on CBD in the originalCBs collection'); + $this->assertCount(1, $transactionDocument['originalCBDs'], 'There should only be on CBD in the originalCBs collection'); $this->assertTransactionDate($transactionDocument, 'startTime'); $this->assertTransactionDate($transactionDocument, 'endTime'); - $this->assertTrue(isset($transactionDocument['changes']), 'Transaction should contain changes'); + $this->assertArrayHasKey('changes', $transactionDocument, 'Transaction should contain changes'); $this->assertChangesForGivenSubject($transactionDocument['changes'], $uri, 1, 0); $expectedCBD = [ '_id' => ['r' => 'http://example.com/resources/1', 'c' => 'http://talisaspire.com/'], @@ -498,10 +506,6 @@ public function testTransactionIsLoggedCorrectlyWhenCompletedSuccessfully() ksort($expectedCBD); $this->assertEquals($expectedCBD, $actualCBD, 'CBD in transaction should match our expected value exactly'); - - // STEP 3 - // update the same entity with a removal - $mTripod = null; $mTripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getDataUpdater']) ->setConstructorArgs(['CBD_testing', 'tripod_php_testing']) @@ -513,25 +517,26 @@ public function testTransactionIsLoggedCorrectlyWhenCompletedSuccessfully() $mTripodUpdate->expects($this->atLeastOnce()) ->method('getUniqId') - ->will($this->returnValue(3)); + ->willReturn('3'); $mTripod->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($mTripodUpdate)); + ->willReturn($mTripodUpdate); $mTripod->setTransactionLog($this->tripodTransactionLog); $g = new MongoGraph(); $g->add_graph($nG); $g->remove_literal_triple($uri, $g->qname_to_uri('dct:title'), 'another title'); + $mTripod->saveChanges($nG, $g, 'http://talisaspire.com/'); // assert this transaction $transactionId = 'transaction_3'; $transactionDocument = $this->getDocument($transactionId, $this->tripod, true); $this->assertEquals($transactionId, $transactionDocument['_id'], 'transtion should have the mocked id we injected'); $this->assertEquals('completed', $transactionDocument['status'], 'status of the transaction should be completed'); - $this->assertEquals(1, count($transactionDocument['originalCBDs']), 'There should only be on CBD in the originalCBs collection'); + $this->assertCount(1, $transactionDocument['originalCBDs'], 'There should only be on CBD in the originalCBs collection'); $this->assertTransactionDate($transactionDocument, 'startTime'); $this->assertTransactionDate($transactionDocument, 'endTime'); - $this->assertTrue(isset($transactionDocument['changes']), 'Transaction should contain changes'); + $this->assertArrayHasKey('changes', $transactionDocument, 'Transaction should contain changes'); $this->assertChangesForGivenSubject($transactionDocument['changes'], $uri, 0, 1); $expectedCBD = [ '_id' => ['r' => 'http://example.com/resources/1', 'c' => 'http://talisaspire.com/'], @@ -550,13 +555,14 @@ public function testTransactionIsLoggedCorrectlyWhenCompletedSuccessfully() $this->assertEquals($expectedCBD, $actualCBD, 'CBD in transaction should match our expected value exactly'); } - public function testTransactionIsLoggedCorrectlyWhenSaveFails() + public function testTransactionIsLoggedCorrectlyWhenSaveFails(): void { $uri = 'http://example.com/resources/1'; // save a new entity, and retrieve it $g = new MongoGraph(); $g->add_resource_triple($uri, $g->qname_to_uri('rdf:type'), $g->qname_to_uri('acorn:Resource')); $g->add_literal_triple($uri, $g->qname_to_uri('dct:title'), 'wibble'); + $mTripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getDataUpdater']) ->setConstructorArgs(['CBD_testing', 'tripod_php_testing']) @@ -568,18 +574,13 @@ public function testTransactionIsLoggedCorrectlyWhenSaveFails() $mTripodUpdate->expects($this->atLeastOnce()) ->method('getUniqId') - ->will($this->returnValue(1)); + ->willReturn('1'); $mTripod->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($mTripodUpdate)); + ->willReturn($mTripodUpdate); $mTripod->setTransactionLog($this->tripodTransactionLog); $mTripod->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/'); - - // STEP 2 - // now attempt to update the entity but throw an exception in applyChangeset - // this should cause the save to fail, and this should be reflected in the transaction log - $mTripod = null; $mTripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getDataUpdater']) ->setConstructorArgs(['CBD_testing', 'tripod_php_testing']) @@ -591,14 +592,14 @@ public function testTransactionIsLoggedCorrectlyWhenSaveFails() $mTripodUpdate->expects($this->atLeastOnce()) ->method('getUniqId') - ->will($this->returnValue(2)); + ->willReturn('2'); $mTripodUpdate->expects($this->atLeastOnce()) ->method('applyChangeSet') - ->will($this->throwException(new Exception('exception thrown by mock test'))); + ->willThrowException(new Exception('exception thrown by mock test')); $mTripod->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($mTripodUpdate)); + ->willReturn($mTripodUpdate); $mTripod->setTransactionLog($this->tripodTransactionLog); $nG = new MongoGraph(); @@ -617,10 +618,10 @@ public function testTransactionIsLoggedCorrectlyWhenSaveFails() $transactionDocument = $this->getDocument($transactionId, $this->tripod, true); $this->assertEquals($transactionId, $transactionDocument['_id'], 'transtion should have the mocked id we injected'); $this->assertEquals('failed', $transactionDocument['status'], 'status of the transaction should be failed'); - $this->assertEquals(1, count($transactionDocument['originalCBDs']), 'There should only be on CBD in the originalCBs collection'); + $this->assertCount(1, $transactionDocument['originalCBDs'], 'There should only be on CBD in the originalCBs collection'); $this->assertTransactionDate($transactionDocument, 'startTime'); $this->assertTransactionDate($transactionDocument, 'failedTime'); - $this->assertTrue(isset($transactionDocument['changes']), 'Transaction should contain changes'); + $this->assertArrayHasKey('changes', $transactionDocument, 'Transaction should contain changes'); $this->assertChangesForGivenSubject($transactionDocument['changes'], $uri, 1, 0); $expectedCBD = [ '_id' => ['r' => 'http://example.com/resources/1', 'c' => 'http://talisaspire.com/'], @@ -643,7 +644,7 @@ public function testTransactionIsLoggedCorrectlyWhenSaveFails() $this->assertNotEmpty($transactionDocument['error']['trace'], 'The transaction log have a non empty error trace'); } - public function testTransactionsLoggedCorrectlyFromMultipleTripods() + public function testTransactionsLoggedCorrectlyFromMultipleTripods(): void { // Create two tripods onto different collection/dbname and make them use the same transaction log $tripod1 = $this->getMockBuilder(Driver::class) @@ -679,8 +680,10 @@ public function testTransactionsLoggedCorrectlyFromMultipleTripods() // change one of the documents $oG = new MongoGraph(); $oG->add_literal_triple($uri, $g->qname_to_uri('searchterms:title'), 'Some title'); + $nG = new MongoGraph(); $nG->add_literal_triple($uri, $g->qname_to_uri('searchterms:title'), 'Changed title'); + $tripod1->saveChanges($oG, $nG, 'http://talisaspire.com/'); // assert the documents and transaction count @@ -692,7 +695,7 @@ public function testTransactionsLoggedCorrectlyFromMultipleTripods() /** * This test ensures that if insertTransaction returns an error, then a Exception is actually thrown. */ - public function testCreateNewTransactionThrowsExceptionIfInsertFails() + public function testCreateNewTransactionThrowsExceptionIfInsertFails(): void { $mockInsert = $this->getMockBuilder(InsertOneResult::class) ->disableOriginalConstructor() @@ -701,7 +704,7 @@ public function testCreateNewTransactionThrowsExceptionIfInsertFails() $mockInsert ->expects($this->once()) ->method('isAcknowledged') - ->will($this->returnValue(false)); + ->willReturn(false); $mockTransactionLog = $this->getMockBuilder(TransactionLog::class) ->onlyMethods(['insertTransaction']) @@ -709,7 +712,7 @@ public function testCreateNewTransactionThrowsExceptionIfInsertFails() ->getMock(); $mockTransactionLog->expects($this->once()) ->method('insertTransaction') - ->will($this->returnValue($mockInsert)); + ->willReturn($mockInsert); try { $mockTransactionLog->createNewTransaction('transaction_1', [], [], 'mydb', 'mycollection'); @@ -722,18 +725,18 @@ public function testCreateNewTransactionThrowsExceptionIfInsertFails() /** * helper method. * - * @param string $id + * @param int $id * @param string $subjectOfChange * @param string $startTime * @param string $endTime * @param int $_version * - * @return array + * @return array */ - protected function buildTransactionDocument($id, $subjectOfChange, $startTime, $endTime, $_version) + protected function buildTransactionDocument($id, $subjectOfChange, $startTime, $endTime, $_version): array { return [ - '_id' => "transaction_{$id}", + '_id' => 'transaction_' . $id, 'changes' => [ [ '_id' => ['r' => '_:cs0', 'c' => 'http://talisaspire.com/'], diff --git a/test/unit/mongo/MongoTripodComputedFieldsTest.php b/test/unit/mongo/MongoTripodComputedFieldsTest.php index d2df981a..6a8c5e75 100644 --- a/test/unit/mongo/MongoTripodComputedFieldsTest.php +++ b/test/unit/mongo/MongoTripodComputedFieldsTest.php @@ -1,5 +1,7 @@ 't_conditional_creators', @@ -81,7 +83,7 @@ public function testConditionalComputedFieldWithDates() $collection->drop(); } - public function testConditionalComputedField() + public function testConditionalComputedField(): void { $tableSpec = [ '_id' => 't_conditional_creators', @@ -145,7 +147,7 @@ public function testConditionalComputedField() $collection->drop(); } - public function testNestedConditionalComputedField() + public function testNestedConditionalComputedField(): void { $tableSpec = [ '_id' => 't_conditional_creators', @@ -229,7 +231,7 @@ public function testNestedConditionalComputedField() $collection->drop(); } - public function testReplaceComputedField() + public function testReplaceComputedField(): void { $tableSpec = [ '_id' => 't_replace_type', @@ -308,7 +310,7 @@ public function testReplaceComputedField() $collection->drop(); } - public function testArithmeticComputedField() + public function testArithmeticComputedField(): void { $tableSpec = [ '_id' => 't_creator_count', @@ -404,7 +406,7 @@ public function testArithmeticComputedField() $collection->drop(); } - public function testNestArithmeticInConditionalIf() + public function testNestArithmeticInConditionalIf(): void { $tableSpec = [ '_id' => 't_conditional_with_nested_arithmetic', @@ -444,7 +446,7 @@ public function testNestArithmeticInConditionalIf() $collection->drop(); } - public function testNestConditionalInArithmeticFunction() + public function testNestConditionalInArithmeticFunction(): void { $tableSpec = [ '_id' => 't_arithmetic_with_nested_conditional', @@ -492,7 +494,7 @@ public function testNestConditionalInArithmeticFunction() $collection->drop(); } - protected function generateUniqueTableId($prefix) + protected function generateUniqueTableId($prefix): string { return uniqid($prefix); } diff --git a/test/unit/mongo/MongoTripodConfigUnitTest.php b/test/unit/mongo/MongoTripodConfigUnitTest.php index fe23cd20..8f9e7f9c 100644 --- a/test/unit/mongo/MongoTripodConfigUnitTest.php +++ b/test/unit/mongo/MongoTripodConfigUnitTest.php @@ -1,5 +1,7 @@ tripodConfig = Config::getInstance(); } - public function testGetInstanceThrowsExceptionIfSetInstanceNotCalledFirst() + public function testGetInstanceThrowsExceptionIfSetInstanceNotCalledFirst(): void { // to test that the instance throws an exception if it is called before calling setConfig // i first have to destroy the instance that is created in the setUp() method of our test suite. @@ -37,10 +37,10 @@ public function testGetInstanceThrowsExceptionIfSetInstanceNotCalledFirst() Config::getInstance(); } - public function testNamespaces() + public function testNamespaces(): void { $ns = $this->tripodConfig->getNamespaces(); - $this->assertEquals(16, count($ns), 'Incorrect number of namespaces'); + $this->assertCount(16, $ns, 'Incorrect number of namespaces'); $expectedNs = []; @@ -60,19 +60,19 @@ public function testNamespaces() $expectedNs['bibo'] = 'http://purl.org/ontology/bibo/'; $expectedNs['foaf'] = 'http://xmlns.com/foaf/0.1/'; $expectedNs['baseData'] = 'http://basedata.com/b/'; - $this->assertEquals($expectedNs, $ns, 'Incorrect namespaces'); + $this->assertSame($expectedNs, $ns, 'Incorrect namespaces'); } - public function testTConfig() + public function testTConfig(): void { $config = Config::getInstance(); - $cfg = Config::getConfig(); + Config::getConfig(); $tConfig = $config->getTransactionLogConfig(); $this->assertEquals('tripod_php_testing', $tConfig['database']); $this->assertEquals('transaction_log', $tConfig['collection']); } - public function testCardinality() + public function testCardinality(): void { $cardinality = $this->tripodConfig->getCardinality('tripod_php_testing', 'CBD_testing', 'dct:created'); $this->assertEquals(1, $cardinality, 'Expected cardinality of 1 for dct:created'); @@ -81,7 +81,7 @@ public function testCardinality() $this->assertEquals(-1, $cardinality, 'Expected cardinality of 1 for random:property'); } - public function testCompoundIndexAllArraysThrowsException() + public function testCompoundIndexAllArraysThrowsException(): void { $this->expectException(ConfigException::class); $this->expectExceptionMessage('Compound index IllegalCompoundIndex has more than one field with cardinality > 1 - mongo will not be able to build this index'); @@ -106,7 +106,8 @@ public function testCompoundIndexAllArraysThrowsException() 'indexes' => [ 'IllegalCompoundIndex' => [ 'rdf:type.value' => 1, - 'dct:subject.value' => 1], + 'dct:subject.value' => 1, + ], ], ], ], @@ -114,18 +115,18 @@ public function testCompoundIndexAllArraysThrowsException() ]; Config::setConfig($config); - $mtc = Config::getInstance(); + Config::getInstance(); } - public function testSearchConfig() + public function testSearchConfig(): void { $config = Config::getInstance(); - $this->assertEquals(MongoSearchProvider::class, $config->getSearchProviderClassName('tripod_php_testing')); + $this->assertSame(MongoSearchProvider::class, $config->getSearchProviderClassName('tripod_php_testing')); - $this->assertEquals(3, count($config->getSearchDocumentSpecifications('tripod_php_testing'))); + $this->assertCount(3, $config->getSearchDocumentSpecifications('tripod_php_testing')); } - public function testCardinalityRuleWithNoNamespace() + public function testCardinalityRuleWithNoNamespace(): void { $this->expectException(ConfigException::class); $this->expectExceptionMessage("Cardinality 'foo:bar' does not have the namespace defined"); @@ -152,10 +153,10 @@ public function testCardinalityRuleWithNoNamespace() ], ]; Config::setConfig($config); - $mtc = Config::getInstance(); + Config::getInstance(); } - public function testGetSearchDocumentSpecificationsByType() + public function testGetSearchDocumentSpecificationsByType(): void { $expectedSpec = [ [ @@ -201,7 +202,7 @@ public function testGetSearchDocumentSpecificationsByType() $this->assertEquals($expectedSpec, $actualSpec); } - public function testGetSearchDocumentSpecificationsById() + public function testGetSearchDocumentSpecificationsById(): void { $expectedSpec = [ @@ -246,14 +247,14 @@ public function testGetSearchDocumentSpecificationsById() $this->assertEquals($expectedSpec, $actualSpec); } - public function testGetSearchDocumentSpecificationsWhereNoneExists() + public function testGetSearchDocumentSpecificationsWhereNoneExists(): void { $expectedSpec = []; $actualSpec = Config::getInstance()->getSearchDocumentSpecifications('something:doesntexist'); - $this->assertEquals($expectedSpec, $actualSpec); + $this->assertSame($expectedSpec, $actualSpec); } - public function testViewSpecCountWithoutTTLThrowsException() + public function testViewSpecCountWithoutTTLThrowsException(): void { $this->expectException(ConfigException::class); $this->expectExceptionMessage('Aggregate function counts exists in spec, but no TTL defined'); @@ -270,8 +271,7 @@ public function testViewSpecCountWithoutTTLThrowsException() 'tripod_php_testing' => [ 'data_source' => 'db', 'pods' => [ - 'CBD_testing' => [ - ], + 'CBD_testing' => [], ], ], ]; @@ -290,10 +290,10 @@ public function testViewSpecCountWithoutTTLThrowsException() ], ]; Config::setConfig($config); - $mtc = Config::getInstance(); + Config::getInstance(); } - public function testViewSpecCountNestedInJoinWithoutTTLThrowsException() + public function testViewSpecCountNestedInJoinWithoutTTLThrowsException(): void { $this->expectException(ConfigException::class); $this->expectExceptionMessage('Aggregate function counts exists in spec, but no TTL defined'); @@ -310,8 +310,7 @@ public function testViewSpecCountNestedInJoinWithoutTTLThrowsException() 'tripod_php_testing' => [ 'data_source' => 'db', 'pods' => [ - 'CBD_testing' => [ - ], + 'CBD_testing' => [], ], ], ]; @@ -333,10 +332,10 @@ public function testViewSpecCountNestedInJoinWithoutTTLThrowsException() ], ]; Config::setConfig($config); - $mtc = Config::getInstance(); + Config::getInstance(); } - public function testTableSpecNestedCountWithoutPropertyThrowsException() + public function testTableSpecNestedCountWithoutPropertyThrowsException(): void { $this->expectException(ConfigException::class); $this->expectExceptionMessage('Count spec does not contain property'); @@ -353,8 +352,7 @@ public function testTableSpecNestedCountWithoutPropertyThrowsException() 'tripod_php_testing' => [ 'data_source' => 'db', 'pods' => [ - 'CBD_testing' => [ - ], + 'CBD_testing' => [], ], ], ]; @@ -374,10 +372,10 @@ public function testTableSpecNestedCountWithoutPropertyThrowsException() ], ]; Config::setConfig($config); - $mtc = Config::getInstance(); + Config::getInstance(); } - public function testTableSpecNested2ndLevelCountWithoutFieldNameThrowsException() + public function testTableSpecNested2ndLevelCountWithoutFieldNameThrowsException(): void { $this->expectException(ConfigException::class); $this->expectExceptionMessage('Count spec does not contain fieldName'); @@ -418,10 +416,10 @@ public function testTableSpecNested2ndLevelCountWithoutFieldNameThrowsException( ], ]; Config::setConfig($config); - $mtc = Config::getInstance(); + Config::getInstance(); } - public function testTableSpecFieldWithoutFieldName() + public function testTableSpecFieldWithoutFieldName(): void { $this->expectException(ConfigException::class); $this->expectExceptionMessage('Field spec does not contain fieldName'); @@ -456,10 +454,10 @@ public function testTableSpecFieldWithoutFieldName() ], ]; Config::setConfig($config); - $mtc = Config::getInstance(); + Config::getInstance(); } - public function testTableSpecFieldWithoutPredicates() + public function testTableSpecFieldWithoutPredicates(): void { $this->expectException(ConfigException::class); $this->expectExceptionMessage('Field spec does not contain predicates'); @@ -494,10 +492,10 @@ public function testTableSpecFieldWithoutPredicates() ], ]; Config::setConfig($config); - $mtc = Config::getInstance(); + Config::getInstance(); } - public function testTableSpecCountWithoutProperty() + public function testTableSpecCountWithoutProperty(): void { $this->expectException(ConfigException::class); $this->expectExceptionMessage('Count spec does not contain property'); @@ -532,10 +530,10 @@ public function testTableSpecCountWithoutProperty() ], ]; Config::setConfig($config); - $mtc = Config::getInstance(); + Config::getInstance(); } - public function testTableSpecCountWithoutFieldName() + public function testTableSpecCountWithoutFieldName(): void { $this->expectException(ConfigException::class); $this->expectExceptionMessage('Count spec does not contain fieldName'); @@ -570,10 +568,10 @@ public function testTableSpecCountWithoutFieldName() ], ]; Config::setConfig($config); - $mtc = Config::getInstance(); + Config::getInstance(); } - public function testTableSpecCountWithoutPropertyAsAString() + public function testTableSpecCountWithoutPropertyAsAString(): void { $this->expectException(ConfigException::class); $this->expectExceptionMessage('Count spec property was not a string'); @@ -609,10 +607,10 @@ public function testTableSpecCountWithoutPropertyAsAString() ], ]; Config::setConfig($config); - $mtc = Config::getInstance(); + Config::getInstance(); } - public function testConfigWithoutDefaultNamespaceThrowsException() + public function testConfigWithoutDefaultNamespaceThrowsException(): void { $this->expectException(ConfigException::class); $this->expectExceptionMessage('Mandatory config key [defaultContext] is missing from config'); @@ -628,8 +626,7 @@ public function testConfigWithoutDefaultNamespaceThrowsException() 'tripod_php_testing' => [ 'data_source' => 'db', 'pods' => [ - 'CBD_testing' => [ - ], + 'CBD_testing' => [], ], ], ]; @@ -650,14 +647,14 @@ public function testConfigWithoutDefaultNamespaceThrowsException() ], ]; Config::setConfig($config); - $mtc = Config::getInstance(); + Config::getInstance(); } /** * the indexesGroupedByCollection method should not only return each of the indexes that are defined explicitly in the config.json, * but also include indexes that are inserted by Config object because they are needed by tripod. */ - public function testGetIndexesGroupedByCollection() + public function testGetIndexesGroupedByCollection(): void { $indexSpecs = Config::getInstance()->getIndexesGroupedByCollection('tripod_php_testing'); @@ -683,7 +680,7 @@ public function testGetIndexesGroupedByCollection() $this->assertEquals(['value._graphs.sioc:has_container.u' => 1, 'value._graphs.sioc:topic.u' => 1], $indexSpecs[VIEWS_COLLECTION]['rs1'][0]); } - public function testGetReplicaSetName() + public function testGetReplicaSetName(): void { $config = []; $config['defaultContext'] = 'http://talisaspire.com/'; @@ -707,15 +704,13 @@ public function testGetReplicaSetName() 'tripod_php_testing' => [ 'data_source' => 'rs1', 'pods' => [ - 'CBD_testing' => [ - ], + 'CBD_testing' => [], ], ], 'testing_2' => [ 'data_source' => 'mongo1', 'pods' => [ - 'CBD_testing' => [ - ], + 'CBD_testing' => [], ], ], ]; @@ -725,10 +720,36 @@ public function testGetReplicaSetName() $mtc = Config::getInstance(); $this->assertEquals('myreplicaset', $mtc->getReplicaSetName($mtc->getDefaultDataSourceForStore('tripod_php_testing'))); - $this->assertNull($mtc->getReplicaSetName('testing_2')); + $this->assertNull($mtc->getReplicaSetName($mtc->getDefaultDataSourceForStore('testing_2'))); + } + + public function testGetReplicaSetNameNonExistingDatasource(): void + { + Config::setConfig([ + 'defaultContext' => 'http://talisaspire.com/', + 'data_sources' => [ + 'tlog' => [ + 'type' => 'mongo', + 'connection' => 'mongodb://abc:zyx@localhost:27018', + ], + ], + 'transaction_log' => [ + 'database' => 'transactions', + 'collection' => 'transaction_log', + 'data_source' => 'tlog', + ], + 'stores' => [], + ]); + + /** @var Tripod\Mongo\Config */ + $mtc = Config::getInstance(); + + $this->expectException(ConfigException::class); + $this->expectExceptionMessage("Data source 'non_existing_data_source' not in configuration"); + $mtc->getReplicaSetName('non_existing_data_source'); } - public function testGetReplicaSetNameFromConnectionString() + public function testGetReplicaSetNameFromConnectionString(): void { Config::setConfig([ 'defaultContext' => 'http://talisaspire.com/', @@ -752,7 +773,7 @@ public function testGetReplicaSetNameFromConnectionString() $this->assertEquals(null, $mtc->getReplicaSetName('rs2')); } - public function testGetViewSpecification() + public function testGetViewSpecification(): void { $expectedVspec = [ '_id' => 'v_resource_full', @@ -788,7 +809,7 @@ public function testGetViewSpecification() $this->assertNull($vspec); } - public function testGetTableSpecification() + public function testGetTableSpecification(): void { $expectedTspec = [ '_id' => 't_resource', @@ -825,7 +846,7 @@ public function testGetTableSpecification() $this->assertNull($tspec); } - public function testSearchConfigNotPresent() + public function testSearchConfigNotPresent(): void { $config = []; $config['defaultContext'] = 'http://talisaspire.com/'; @@ -848,15 +869,15 @@ public function testSearchConfigNotPresent() Config::setConfig($config); $mtc = Config::getInstance(); $this->assertNull($mtc->getSearchProviderClassName('tripod_php_testing')); - $this->assertEquals([], $mtc->getSearchDocumentSpecifications('tripod_php_testing')); + $this->assertSame([], $mtc->getSearchDocumentSpecifications('tripod_php_testing')); } - public function testGetAllTypesInSpecifications() + public function testGetAllTypesInSpecifications(): void { $types = $this->tripodConfig->getAllTypesInSpecifications('tripod_php_testing'); - $this->assertEquals( + $this->assertCount( 12, - count($types), + $types, 'There should be 12 types based on the configured view, table and search specifications in config.json' ); $expectedValues = [ @@ -875,14 +896,14 @@ public function testGetAllTypesInSpecifications() ]; foreach ($expectedValues as $expected) { - $this->assertContains($expected, $types, "List of types should have contained {$expected}"); + $this->assertContains($expected, $types, 'List of types should have contained ' . $expected); } } - public function testGetPredicatesForTableSpec() + public function testGetPredicatesForTableSpec(): void { $predicates = $this->tripodConfig->getDefinedPredicatesInSpec('tripod_php_testing', 't_users'); - $this->assertEquals(6, count($predicates), 'There should be 6 predicates defined in t_users in config.json'); + $this->assertCount(6, $predicates, 'There should be 6 predicates defined in t_users in config.json'); $expectedValues = [ 'rdf:type', 'foaf:firstName', @@ -893,14 +914,14 @@ public function testGetPredicatesForTableSpec() ]; foreach ($expectedValues as $expected) { - $this->assertContains($expected, $predicates, "List of predicates should have contained {$expected}"); + $this->assertContains($expected, $predicates, 'List of predicates should have contained ' . $expected); } } - public function testGetPredicatesForSearchDocSpec() + public function testGetPredicatesForSearchDocSpec(): void { $predicates = $this->tripodConfig->getDefinedPredicatesInSpec('tripod_php_testing', 'i_search_list'); - $this->assertEquals(6, count($predicates), 'There should be 6 predicates defined in i_search_list in config.json'); + $this->assertCount(6, $predicates, 'There should be 6 predicates defined in i_search_list in config.json'); $expectedValues = [ 'rdf:type', @@ -912,15 +933,15 @@ public function testGetPredicatesForSearchDocSpec() ]; foreach ($expectedValues as $expected) { - $this->assertContains($expected, $predicates, "List of predicates should have contained {$expected}"); + $this->assertContains($expected, $predicates, 'List of predicates should have contained ' . $expected); } } - public function testGetPredicatesForSpecFilter() + public function testGetPredicatesForSpecFilter(): void { $predicates = $this->tripodConfig->getDefinedPredicatesInSpec('tripod_php_testing', 'i_search_filter_parse'); - $this->assertEquals(6, count($predicates), 'There should be 6 predicates defined in i_search_filter_parse in config.json'); + $this->assertCount(6, $predicates, 'There should be 6 predicates defined in i_search_filter_parse in config.json'); $expectedValues = [ 'rdf:type', @@ -932,11 +953,11 @@ public function testGetPredicatesForSpecFilter() ]; foreach ($expectedValues as $expected) { - $this->assertContains($expected, $predicates, "List of predicates should have contained {$expected}"); + $this->assertContains($expected, $predicates, 'List of predicates should have contained ' . $expected); } } - public function testCollectionReadPreferencesAreAppliedToDatabase() + public function testCollectionReadPreferencesAreAppliedToDatabase(): void { $mockConfig = $this->getMockBuilder(TripodTestConfig::class) ->onlyMethods(['getDatabase']) @@ -948,19 +969,17 @@ public function testCollectionReadPreferencesAreAppliedToDatabase() ['tripod_php_testing', 'rs1', ReadPreference::RP_SECONDARY_PREFERRED], ['tripod_php_testing', 'rs1', ReadPreference::RP_NEAREST] ) - ->will($this->returnCallback( - function () { - $mongo = new Client(null); + ->willReturnCallback(function () { + $mongo = new Client(); - return $mongo->selectDatabase('tripod_php_testing'); - } - )); + return $mongo->selectDatabase('tripod_php_testing'); + }); $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::RP_SECONDARY_PREFERRED); $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::RP_NEAREST); } - public function testDataLoadedInConfiguredDataSource() + public function testDataLoadedInConfiguredDataSource(): void { $storeName = 'tripod_php_testing'; @@ -976,13 +995,13 @@ public function testDataLoadedInConfiguredDataSource() } } - foreach ($config->getViewSpecifications($storeName) as $id => $spec) { + foreach ($config->getViewSpecifications($storeName) as $spec) { if (!in_array($spec['to_data_source'], $dataSourcesForStore)) { $dataSourcesForStore[] = $spec['to_data_source']; } } - foreach ($config->getTableSpecifications($storeName) as $id => $spec) { + foreach ($config->getTableSpecifications($storeName) as $spec) { if (!in_array($spec['to_data_source'], $dataSourcesForStore)) { $dataSourcesForStore[] = $spec['to_data_source']; } @@ -1009,10 +1028,11 @@ public function testDataLoadedInConfiguredDataSource() break; } + $config->getDatabase($storeName, $source)->drop(); } - if ($diff == false) { + if ($diff === false) { $this->markTestSkipped('All datasources configured for store use same configuration, nothing to test'); } @@ -1024,16 +1044,19 @@ public function testDataLoadedInConfiguredDataSource() $labeller = new Labeller(); $graph->add_resource_triple($subject, RDF_TYPE, $labeller->qname_to_uri('foaf:Person')); $graph->add_literal_triple($subject, FOAF_NAME, 'Anne Example'); + $this->tripod->saveChanges(new ExtendedGraph(), $graph); $newGraph = $this->tripod->describeResource($subject); $newGraph->add_literal_triple($subject, $labeller->qname_to_uri('foaf:email'), 'anne@example.com'); + $this->tripod->saveChanges($graph, $newGraph); // Generate views and tables foreach ($config->getViewSpecifications($storeName) as $viewId => $viewSpec) { $this->tripod->getTripodViews()->generateView($viewId); } + foreach ($config->getTableSpecifications($storeName) as $tableId => $tableSpec) { $this->tripod->generateTableRows($tableId); } @@ -1043,12 +1066,17 @@ public function testDataLoadedInConfiguredDataSource() $lCollection->drop(); $lCollection->insertOne([_ID_KEY => [_ID_RESOURCE => 'foo', _ID_CONTEXT => 'bar'], _LOCKED_FOR_TRANS => 'foobar']); $lCollection->insertOne([_ID_KEY => [_ID_RESOURCE => 'baz', _ID_CONTEXT => 'bar'], _LOCKED_FOR_TRANS => 'wibble']); + $this->tripod->removeInertLocks('foobar', 'reason1'); $collectionsForDataSource = []; $collectionsForDataSource['rs1'] = [ - VIEWS_COLLECTION, SEARCH_INDEX_COLLECTION, TABLE_ROWS_COLLECTION, 'CBD_testing', - AUDIT_MANUAL_ROLLBACKS_COLLECTION, LOCKS_COLLECTION, + VIEWS_COLLECTION, + SEARCH_INDEX_COLLECTION, + TABLE_ROWS_COLLECTION, + 'CBD_testing', + AUDIT_MANUAL_ROLLBACKS_COLLECTION, + LOCKS_COLLECTION, ]; $collectionsForDataSource['rs2'] = [VIEWS_COLLECTION, SEARCH_INDEX_COLLECTION, TABLE_ROWS_COLLECTION, 'CBD_testing_2', 'transaction_log']; @@ -1063,6 +1091,7 @@ public function testDataLoadedInConfiguredDataSource() if (!isset($specsForDataSource[$spec['to_data_source']][$type])) { $specsForDataSource[$spec['to_data_source']][$type] = []; } + $specsForDataSource[$spec['to_data_source']][$type][] = $spec['_id']; } } @@ -1086,6 +1115,7 @@ public function testDataLoadedInConfiguredDataSource() if ($otherSource == $source) { continue; } + foreach ($specsForDataSource[$otherSource]['views'] as $view) { $this->assertEquals(0, $collection->count(['_id.type' => $view]), $view . ' had at least 1 document in data source ' . $source); } @@ -1102,6 +1132,7 @@ public function testDataLoadedInConfiguredDataSource() if ($otherSource == $source) { continue; } + foreach ($specsForDataSource[$otherSource]['search'] as $search) { $this->assertEquals(0, $collection->count(['_id.type' => $search]), $search . ' had at least 1 document in data source ' . $source); } @@ -1117,6 +1148,7 @@ public function testDataLoadedInConfiguredDataSource() if ($otherSource == $source) { continue; } + foreach ($specsForDataSource[$otherSource]['table_rows'] as $t) { $this->assertEquals(0, $collection->count(['_id.type' => $t]), $t . ' had at least 1 document in data source ' . $source); } @@ -1138,7 +1170,7 @@ public function testDataLoadedInConfiguredDataSource() } } - public function testTransactionLogIsWrittenToCorrectDBAndCollection() + public function testTransactionLogIsWrittenToCorrectDBAndCollection(): void { $storeName = 'tripod_php_testing'; $newConfig = Config::getConfig(); @@ -1180,10 +1212,12 @@ public function testTransactionLogIsWrittenToCorrectDBAndCollection() $labeller = new Labeller(); $graph->add_resource_triple($subject, RDF_TYPE, $labeller->qname_to_uri('foaf:Person')); $graph->add_literal_triple($subject, FOAF_NAME, 'Anne Example'); + $this->tripod->saveChanges(new ExtendedGraph(), $graph); $newGraph = $this->tripod->describeResource($subject); $newGraph->add_literal_triple($subject, $labeller->qname_to_uri('foaf:email'), 'anne@example.com'); + $this->tripod->saveChanges($graph, $newGraph); // Make sure the dbs do now exist @@ -1194,6 +1228,7 @@ public function testTransactionLogIsWrittenToCorrectDBAndCollection() $transactionDbExists = true; } } + $this->assertTrue($transactionDbExists); // Make sure the data in the dbs look right @@ -1201,10 +1236,10 @@ public function testTransactionLogIsWrittenToCorrectDBAndCollection() $transactionCount = $transactionColletion->count(); $transactionExampleDocument = $transactionColletion->findOne(); $this->assertEquals(26, $transactionCount); - $this->assertStringContainsString('transaction_', $transactionExampleDocument['_id']); + $this->assertStringContainsString('transaction_', (string) $transactionExampleDocument['_id']); } - public function testComputedFieldSpecValidationInvalidFunction() + public function testComputedFieldSpecValidationInvalidFunction(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1216,7 +1251,7 @@ public function testComputedFieldSpecValidationInvalidFunction() Config::getInstance(); } - public function testComputedFieldSpecValidationMultipleFunctions() + public function testComputedFieldSpecValidationMultipleFunctions(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1228,7 +1263,7 @@ public function testComputedFieldSpecValidationMultipleFunctions() Config::getInstance(); } - public function testComputedFieldSpecValidationMustBeAtBaseLevel() + public function testComputedFieldSpecValidationMustBeAtBaseLevel(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1240,7 +1275,7 @@ public function testComputedFieldSpecValidationMustBeAtBaseLevel() Config::getInstance(); } - public function testConditionalSpecValidationEmptyConditional() + public function testConditionalSpecValidationEmptyConditional(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1252,7 +1287,7 @@ public function testConditionalSpecValidationEmptyConditional() Config::getInstance(); } - public function testConditionalSpecValidationMissingThenElse() + public function testConditionalSpecValidationMissingThenElse(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1264,7 +1299,7 @@ public function testConditionalSpecValidationMissingThenElse() Config::getInstance(); } - public function testConditionalSpecValidationEmptyIf() + public function testConditionalSpecValidationEmptyIf(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1276,7 +1311,7 @@ public function testConditionalSpecValidationEmptyIf() Config::getInstance(); } - public function testConditionalSpecValidationIfNotArray() + public function testConditionalSpecValidationIfNotArray(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1288,7 +1323,7 @@ public function testConditionalSpecValidationIfNotArray() Config::getInstance(); } - public function testConditionalSpecValidationIfHasTwoValues() + public function testConditionalSpecValidationIfHasTwoValues(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1300,7 +1335,7 @@ public function testConditionalSpecValidationIfHasTwoValues() Config::getInstance(); } - public function testConditionalSpecValidationIfHasMoreThanThreeValues() + public function testConditionalSpecValidationIfHasMoreThanThreeValues(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1312,7 +1347,7 @@ public function testConditionalSpecValidationIfHasMoreThanThreeValues() Config::getInstance(); } - public function testConditionalSpecValidationIfHasInvalidConditionalOperator() + public function testConditionalSpecValidationIfHasInvalidConditionalOperator(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1324,7 +1359,7 @@ public function testConditionalSpecValidationIfHasInvalidConditionalOperator() Config::getInstance(); } - public function testConditionalSpecValidationIfHasInvalidVariableAsLeftOperand() + public function testConditionalSpecValidationIfHasInvalidVariableAsLeftOperand(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1336,7 +1371,7 @@ public function testConditionalSpecValidationIfHasInvalidVariableAsLeftOperand() Config::getInstance(); } - public function testConditionalSpecValidationIfHasInvalidVariableAsRightOperand() + public function testConditionalSpecValidationIfHasInvalidVariableAsRightOperand(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1348,7 +1383,7 @@ public function testConditionalSpecValidationIfHasInvalidVariableAsRightOperand( Config::getInstance(); } - public function testConditionalSpecValidationThenHasInvalidVariable() + public function testConditionalSpecValidationThenHasInvalidVariable(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1360,7 +1395,7 @@ public function testConditionalSpecValidationThenHasInvalidVariable() Config::getInstance(); } - public function testConditionalSpecValidationElseHasInvalidVariable() + public function testConditionalSpecValidationElseHasInvalidVariable(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1372,7 +1407,7 @@ public function testConditionalSpecValidationElseHasInvalidVariable() Config::getInstance(); } - public function testReplaceSpecValidationEmptyFunction() + public function testReplaceSpecValidationEmptyFunction(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1384,7 +1419,7 @@ public function testReplaceSpecValidationEmptyFunction() Config::getInstance(); } - public function testReplaceSpecValidationMissingReplace() + public function testReplaceSpecValidationMissingReplace(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1396,7 +1431,7 @@ public function testReplaceSpecValidationMissingReplace() Config::getInstance(); } - public function testReplaceSpecValidationMissingSubject() + public function testReplaceSpecValidationMissingSubject(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1408,7 +1443,7 @@ public function testReplaceSpecValidationMissingSubject() Config::getInstance(); } - public function testReplaceSpecValidationInvalidVariableInSearch() + public function testReplaceSpecValidationInvalidVariableInSearch(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1420,7 +1455,7 @@ public function testReplaceSpecValidationInvalidVariableInSearch() Config::getInstance(); } - public function testReplaceSpecValidationInvalidVariableInSearchArray() + public function testReplaceSpecValidationInvalidVariableInSearchArray(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1432,7 +1467,7 @@ public function testReplaceSpecValidationInvalidVariableInSearchArray() Config::getInstance(); } - public function testReplaceSpecValidationInvalidVariableInReplace() + public function testReplaceSpecValidationInvalidVariableInReplace(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1444,7 +1479,7 @@ public function testReplaceSpecValidationInvalidVariableInReplace() Config::getInstance(); } - public function testReplaceSpecValidationInvalidVariableInReplaceArray() + public function testReplaceSpecValidationInvalidVariableInReplaceArray(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1456,7 +1491,7 @@ public function testReplaceSpecValidationInvalidVariableInReplaceArray() Config::getInstance(); } - public function testReplaceSpecValidationInvalidVariableInSubject() + public function testReplaceSpecValidationInvalidVariableInSubject(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1468,7 +1503,7 @@ public function testReplaceSpecValidationInvalidVariableInSubject() Config::getInstance(); } - public function testReplaceSpecValidationInvalidVariableInSubjectArray() + public function testReplaceSpecValidationInvalidVariableInSubjectArray(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1480,7 +1515,7 @@ public function testReplaceSpecValidationInvalidVariableInSubjectArray() Config::getInstance(); } - public function testArithmeticSpecValidationEmptyFunction() + public function testArithmeticSpecValidationEmptyFunction(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1492,7 +1527,7 @@ public function testArithmeticSpecValidationEmptyFunction() Config::getInstance(); } - public function testArithmeticSpecValidationOneValue() + public function testArithmeticSpecValidationOneValue(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1504,7 +1539,7 @@ public function testArithmeticSpecValidationOneValue() Config::getInstance(); } - public function testArithmeticSpecValidationTwoValues() + public function testArithmeticSpecValidationTwoValues(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1516,7 +1551,7 @@ public function testArithmeticSpecValidationTwoValues() Config::getInstance(); } - public function testArithmeticSpecValidationFourValues() + public function testArithmeticSpecValidationFourValues(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1528,7 +1563,7 @@ public function testArithmeticSpecValidationFourValues() Config::getInstance(); } - public function testArithmeticSpecValidationInvalidArithmeticOperator() + public function testArithmeticSpecValidationInvalidArithmeticOperator(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1540,7 +1575,7 @@ public function testArithmeticSpecValidationInvalidArithmeticOperator() Config::getInstance(); } - public function testArithmeticSpecValidationInvalidVariableLeftOperand() + public function testArithmeticSpecValidationInvalidVariableLeftOperand(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1552,7 +1587,7 @@ public function testArithmeticSpecValidationInvalidVariableLeftOperand() Config::getInstance(); } - public function testArithmeticSpecValidationInvalidVariableRightOperand() + public function testArithmeticSpecValidationInvalidVariableRightOperand(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1564,7 +1599,7 @@ public function testArithmeticSpecValidationInvalidVariableRightOperand() Config::getInstance(); } - public function testArithmeticSpecValidationInvalidNestedVariable() + public function testArithmeticSpecValidationInvalidNestedVariable(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1576,7 +1611,7 @@ public function testArithmeticSpecValidationInvalidNestedVariable() Config::getInstance(); } - public function testArithmeticSpecValidationInvalidNestedOperator() + public function testArithmeticSpecValidationInvalidNestedOperator(): void { $newConfig = Config::getConfig(); Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); @@ -1588,18 +1623,19 @@ public function testArithmeticSpecValidationInvalidNestedOperator() Config::getInstance(); } - public function testGetResqueServer() + public function testGetResqueServer(): void { Tripod\Mongo\Config::setValidationLevel(Tripod\Mongo\Config::VALIDATE_MAX); if (!getenv(MONGO_TRIPOD_RESQUE_SERVER)) { putenv(MONGO_TRIPOD_RESQUE_SERVER . '=redis'); } + $this->assertEquals(getenv(MONGO_TRIPOD_RESQUE_SERVER), Tripod\Mongo\Config::getResqueServer()); } // MongoClient creation tests - public function testMongoConnectionNoExceptions() + public function testMongoConnectionNoExceptions(): void { $mockConfig = $this->getMockBuilder(TripodTestConfig::class) ->onlyMethods(['getMongoClient']) @@ -1608,17 +1644,13 @@ public function testMongoConnectionNoExceptions() $mockConfig->expects($this->exactly(1)) ->method('getMongoClient') ->with('mongodb://mongodb:27017/', ['connectTimeoutMS' => 20000]) - ->will($this->returnCallback( - function () { - return new Client(); - } - )); + ->willReturnCallback(fn (): Client => new Client()); $mockConfig->getDatabase('tripod_php_testing', 'rs1', ReadPreference::RP_SECONDARY_PREFERRED); $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::RP_SECONDARY_PREFERRED); $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::RP_NEAREST); } - public function testMongoConnectionExceptionThrown() + public function testMongoConnectionExceptionThrown(): void { $this->expectException(ConnectionTimeoutException::class); $this->expectExceptionMessage('Exception thrown when connecting to Mongo'); @@ -1629,12 +1661,12 @@ public function testMongoConnectionExceptionThrown() $mockConfig->expects($this->exactly(30)) ->method('getMongoClient') ->with('mongodb://mongodb:27017/', ['connectTimeoutMS' => 20000]) - ->will($this->throwException(new ConnectionTimeoutException('Exception thrown when connecting to Mongo'))); + ->willThrowException(new ConnectionTimeoutException('Exception thrown when connecting to Mongo')); $mockConfig->getDatabase('tripod_php_testing', 'rs1', ReadPreference::RP_SECONDARY_PREFERRED); } - public function testMongoConnectionNoExceptionThrownWhenConnectionThrowsSomeExceptions() + public function testMongoConnectionNoExceptionThrownWhenConnectionThrowsSomeExceptions(): void { $mockConfig = $this->getMockBuilder(TripodTestConfig::class) ->onlyMethods(['getMongoClient']) @@ -1642,18 +1674,15 @@ public function testMongoConnectionNoExceptionThrownWhenConnectionThrowsSomeExce $mockConfig->loadConfig(json_decode(file_get_contents(__DIR__ . '/data/config.json'), true)); $mockConfig->expects($this->exactly(5)) ->method('getMongoClient') - ->with('mongodb://mongodb:27017/', ['connectTimeoutMS' => 20000]) - ->will($this->onConsecutiveCalls( + ->with('mongodb://mongodb:27017/', ['connectTimeoutMS' => 20000])->willReturnOnConsecutiveCalls( $this->throwException(new ConnectionTimeoutException('Exception thrown when connecting to Mongo')), $this->throwException(new ConnectionTimeoutException('Exception thrown when connecting to Mongo')), $this->throwException(new ConnectionTimeoutException('Exception thrown when connecting to Mongo')), $this->throwException(new ConnectionTimeoutException('Exception thrown when connecting to Mongo')), $this->returnCallback( - function () { - return new Client(); - } + fn (): Client => new Client() ) - )); + ); $mockConfig->getDatabase('tripod_php_testing', 'rs1', ReadPreference::RP_SECONDARY_PREFERRED); $mockConfig->getCollectionForCBD('tripod_php_testing', 'CBD_testing', ReadPreference::RP_SECONDARY_PREFERRED); diff --git a/test/unit/mongo/MongoTripodDocumentStructureTest.php b/test/unit/mongo/MongoTripodDocumentStructureTest.php index 08199b36..ec821a6b 100644 --- a/test/unit/mongo/MongoTripodDocumentStructureTest.php +++ b/test/unit/mongo/MongoTripodDocumentStructureTest.php @@ -1,5 +1,7 @@ loadResourceDataViaTripod(); } - public function testDocumentContainsDefaultProperties() + public function testDocumentContainsDefaultProperties(): void { $id = ['r' => 'http://talisaspire.com/resources/testDocument', 'c' => 'http://talisaspire.com/']; $graph = new MongoGraph(); $graph->add_literal_triple($id['r'], $graph->qname_to_uri('searchterms:title'), 'TEST TITLE'); + $this->tripod->saveChanges(new MongoGraph(), $graph); $this->assertDocumentExists($id); @@ -50,13 +53,14 @@ public function testDocumentContainsDefaultProperties() $this->assertDocumentHasProperty($id, _CREATED_TS); } - public function testDocumentTimeStampsAreUpdatedCorrectlyAfterMultipleWritesAndDelete() + public function testDocumentTimeStampsAreUpdatedCorrectlyAfterMultipleWritesAndDelete(): void { // create an initial document $id = ['r' => 'http://talisaspire.com/resources/testDocument', 'c' => 'http://talisaspire.com/']; $graph = new MongoGraph(); $graph->add_literal_triple($id['r'], $graph->qname_to_uri('searchterms:title'), 'TEST TITLE'); + $this->tripod->saveChanges(new MongoGraph(), $graph); // assert that it is at version 0 $this->assertDocumentExists($id); @@ -72,6 +76,7 @@ public function testDocumentTimeStampsAreUpdatedCorrectlyAfterMultipleWritesAndD // change document through tripod $newGraph = new MongoGraph(); $newGraph->add_literal_triple($id['r'], $graph->qname_to_uri('searchterms:title'), 'CHANGED TITLE'); + $this->tripod->saveChanges($graph, $newGraph); // assert that it is at version 1 @@ -91,6 +96,7 @@ public function testDocumentTimeStampsAreUpdatedCorrectlyAfterMultipleWritesAndD // update again $finalGraph = new MongoGraph(); $finalGraph->add_literal_triple($id['r'], $graph->qname_to_uri('searchterms:title'), 'CHANGED TITLE AGAIN'); + $this->tripod->saveChanges($newGraph, $finalGraph); // assert that it is at version 2 @@ -125,7 +131,7 @@ public function testDocumentTimeStampsAreUpdatedCorrectlyAfterMultipleWritesAndD * This test verifies that if a document was previously added to mongo without any timestamps i.e. _UPDATED_TS and _CREATED_TS * then on a tripod write only the _UPDATED_TS will be added to the document. */ - public function testOnlyDocumentUpdatedTimestampIsAddedToDocumentThatDidntHaveTimestampsToBeginWith() + public function testOnlyDocumentUpdatedTimestampIsAddedToDocumentThatDidntHaveTimestampsToBeginWith(): void { // add the initial document, but not through Driver! $_id = ['r' => 'http://talisaspire.com/resources/testDocument2', 'c' => 'http://talisaspire.com/']; @@ -147,6 +153,7 @@ public function testOnlyDocumentUpdatedTimestampIsAddedToDocumentThatDidntHaveTi // change the document through tripod, for this im just doing a new addition $graph = new MongoGraph(); $graph->add_literal_triple($_id['r'], $graph->qname_to_uri('searchterms:title'), 'a new property'); + $this->tripod->saveChanges(new MongoGraph(), $graph); // Now assert, document should contain the additiona triple we added, an updated _version. diff --git a/test/unit/mongo/MongoTripodDriverTest.php b/test/unit/mongo/MongoTripodDriverTest.php index 77a3a9ae..d6668ce2 100755 --- a/test/unit/mongo/MongoTripodDriverTest.php +++ b/test/unit/mongo/MongoTripodDriverTest.php @@ -1,7 +1,8 @@ loadResourceDataViaTripod(); } - public function testSelectMultiValue() + public function testSelectMultiValue(): void { $expectedResult = [ 'head' => [ @@ -78,7 +79,8 @@ public function testSelectMultiValue() [ '_id' => [ _ID_RESOURCE => 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', - _ID_CONTEXT => 'http://talisaspire.com/'], + _ID_CONTEXT => 'http://talisaspire.com/', + ], 'dct:source' => [ 'http://life.ac.uk/resources/BFBC6A06-A8B0-DED8-53AA-8E80DB44CC53', 'http://life.ac.uk/resources/836E7CAD-63D2-63A0-B1CB-AA6A7E54A5C9', @@ -90,7 +92,7 @@ public function testSelectMultiValue() $this->assertEquals($expectedResult, $actualResult); } - public function testSelectSingleValue() + public function testSelectSingleValue(): void { $expectedResult = [ 'head' => [ @@ -102,7 +104,8 @@ public function testSelectSingleValue() [ '_id' => [ _ID_RESOURCE => 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', - _ID_CONTEXT => 'http://talisaspire.com/'], + _ID_CONTEXT => 'http://talisaspire.com/', + ], 'dct:subject' => 'http://talisaspire.com/disciplines/physics', ], ], @@ -111,7 +114,7 @@ public function testSelectSingleValue() $this->assertEquals($expectedResult, $actualResult); } - public function testGraph() + public function testGraph(): void { $expectedResult = new ExtendedGraph(); $expectedResult->add_turtle( @@ -152,7 +155,7 @@ public function testGraph() $this->assertFalse($cs->has_changes()); } - public function testDescribeResource() + public function testDescribeResource(): void { $expectedResult = new ExtendedGraph(); $expectedResult->add_turtle( @@ -193,7 +196,7 @@ public function testDescribeResource() $this->assertFalse($cs->has_changes()); } - public function testDescribeResources() + public function testDescribeResources(): void { $expectedResult = new ExtendedGraph(); $expectedResult->add_turtle( @@ -241,13 +244,13 @@ public function testDescribeResources() $this->assertFalse($cs->has_changes()); } - public function testGetCount() + public function testGetCount(): void { $count = $this->tripod->getCount(['rdf:type.' . VALUE_URI => 'bibo:Book']); $this->assertEquals(9, $count); } - public function testGetCountWithGroupBy() + public function testGetCountWithGroupBy(): void { $count = $this->tripod->getCount(['rdf:type.' . VALUE_URI => 'bibo:Book'], 'bibo:isbn13.l'); @@ -259,7 +262,7 @@ public function testGetCountWithGroupBy() $this->assertEquals(1, $count['9780393929690']); } - public function testTripodSaveChangesRemovesLiteralTriple() + public function testTripodSaveChangesRemovesLiteralTriple(): void { $oG = $this->tripod->describeResource('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA'); $nG = new MongoGraph(); @@ -271,7 +274,7 @@ public function testTripodSaveChangesRemovesLiteralTriple() $this->assertEquals($nG, $uG, 'Updated does not match expected graph'); } - public function testTripodSaveChangesAddsLiteralTriple() + public function testTripodSaveChangesAddsLiteralTriple(): void { $oG = $this->tripod->describeResource('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA'); $nG = new MongoGraph(); @@ -288,10 +291,11 @@ public function testTripodSaveChangesAddsLiteralTriple() * we dont have to load the whole document in as the old graph, we just enumerate the single triple we want removed * what should happen is that the cs builder will translate that into a single removal. */ - public function testTripodSaveChangesRemovesLiteralTripleUsingEmptyNewGraphAndPartialOldGraph() + public function testTripodSaveChangesRemovesLiteralTripleUsingEmptyNewGraphAndPartialOldGraph(): void { $oG = new MongoGraph(); $oG->add_literal_triple('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', $oG->qname_to_uri('bibo:isbn13'), '9780393929690'); + $nG = new MongoGraph(); $this->tripod->saveChanges($oG, $nG, 'http://talisaspire.com/', 'my changes'); @@ -306,7 +310,7 @@ public function testTripodSaveChangesRemovesLiteralTripleUsingEmptyNewGraphAndPa * this test verifies that if we simply want to add some data to a document that exists in we dont need to specify an oldgraph; we just need to specify the new graph * the cs builder should translate that into a single addition statement and apply it. */ - public function testTripodSaveChangesAddsLiteralTripleUsingEmptyOldGraph() + public function testTripodSaveChangesAddsLiteralTripleUsingEmptyOldGraph(): void { $oG = new MongoGraph(); $nG = new MongoGraph(); @@ -318,7 +322,7 @@ public function testTripodSaveChangesAddsLiteralTripleUsingEmptyOldGraph() $this->assertHasLiteralTriple($uG, 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', $nG->qname_to_uri('searchterms:title'), 'TEST TITLE'); } - public function testTripodSaveChangesUpdatesLiteralTriple() + public function testTripodSaveChangesUpdatesLiteralTriple(): void { $oG = $this->tripod->describeResource('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA'); $nG = new MongoGraph(); @@ -335,13 +339,14 @@ public function testTripodSaveChangesUpdatesLiteralTriple() $this->assertFalse($uG->has_literal_triple('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', $nG->qname_to_uri('searchterms:title'), 'Physics 3rd Edition'), 'Graph should not contain literal triple we removed'); } - public function testSaveCompletelyNewGraph() + public function testSaveCompletelyNewGraph(): void { $uri = 'http://example.com/resources/1'; $g = new MongoGraph(); $g->add_resource_triple($uri, $g->qname_to_uri('rdf:type'), $g->qname_to_uri('acorn:Resource')); $g->add_literal_triple($uri, $g->qname_to_uri('dct:title'), 'wibble'); + $this->tripod->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/', 'something new'); $uG = $this->tripod->describeResource($uri); @@ -351,7 +356,7 @@ public function testSaveCompletelyNewGraph() $this->assertTrue($uG->has_literal_triple($uri, $g->qname_to_uri('dct:title'), 'wibble'), 'Graph should contain literal triple we added'); } - public function testRemoveGraphEntirely() + public function testRemoveGraphEntirely(): void { $uri = 'http://example.com/resources/1'; @@ -359,6 +364,7 @@ public function testRemoveGraphEntirely() $g = new MongoGraph(); $g->add_resource_triple($uri, $g->qname_to_uri('rdf:type'), $g->qname_to_uri('acorn:Resource')); $g->add_literal_triple($uri, $g->qname_to_uri('dct:title'), 'wibble'); + $this->tripod->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/', 'something new'); // retrieve it and make sure it was saved correctly @@ -374,7 +380,7 @@ public function testRemoveGraphEntirely() $this->assertTrue($g->is_empty()); } - public function testSaveFailsWhenOldGraphIsInvalidNoDataInStoreForObj() + public function testSaveFailsWhenOldGraphIsInvalidNoDataInStoreForObj(): void { $this->expectException(Exception::class); $this->expectExceptionMessage('Error storing changes'); @@ -388,10 +394,10 @@ public function testSaveFailsWhenOldGraphIsInvalidNoDataInStoreForObj() $nG = new MongoGraph(); $nG->add_resource_triple($uri, $nG->qname_to_uri('rdf:type'), $nG->qname_to_uri('acorn:List')); - $result = $this->tripod->saveChanges($oG, $nG, 'http://talisaspire.com/'); + $this->tripod->saveChanges($oG, $nG, 'http://talisaspire.com/'); } - public function testInterleavingUpdateFailsIfUnderlyingDataHasChanged() + public function testInterleavingUpdateFailsIfUnderlyingDataHasChanged(): void { $this->expectException(Exception::class); $this->expectExceptionMessage('Error storing changes'); @@ -419,22 +425,23 @@ public function testInterleavingUpdateFailsIfUnderlyingDataHasChanged() $mockTripodUpdate->expects($this->once()) ->method('getDocumentForUpdate') ->with($uri) - ->will($this->returnValue($doc)); + ->willReturn($doc); $mockTripod->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdate)); + ->willReturn($mockTripodUpdate); $mockTripod->setTransactionLog($this->tripodTransactionLog); $oG = new MongoGraph(); $oG->add_resource_triple($uri, $oG->qname_to_uri('rdf:type'), $oG->qname_to_uri('acorn:Book')); + $nG = new MongoGraph(); $nG->add_resource_triple($uri, $nG->qname_to_uri('rdf:type'), $nG->qname_to_uri('acorn:Foo')); - $result = $mockTripod->saveChanges($oG, $nG, 'http://talisaspire.com/'); + $mockTripod->saveChanges($oG, $nG, 'http://talisaspire.com/'); } - public function testInterleavingUpdateFailsIfCriteriaIsNotValidAtPointOfSave() + public function testInterleavingUpdateFailsIfCriteriaIsNotValidAtPointOfSave(): void { $this->expectException(Exception::class); $this->expectExceptionMessage('Error storing changes'); @@ -444,6 +451,7 @@ public function testInterleavingUpdateFailsIfCriteriaIsNotValidAtPointOfSave() $g = new MongoGraph(); $g->add_resource_triple($uri, $g->qname_to_uri('rdf:type'), $g->qname_to_uri('acorn:Book')); + $this->tripod->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/'); // canned response will simulate that the underlying data has changed @@ -466,11 +474,11 @@ public function testInterleavingUpdateFailsIfCriteriaIsNotValidAtPointOfSave() $mockTripodUpdate->expects($this->once()) ->method('getDocumentForUpdate') ->with($uri) - ->will($this->returnValue($doc)); + ->willReturn($doc); $mockTripod->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdate)); + ->willReturn($mockTripodUpdate); $mockTripod->setTransactionLog($this->tripodTransactionLog); @@ -480,10 +488,10 @@ public function testInterleavingUpdateFailsIfCriteriaIsNotValidAtPointOfSave() $nG = new MongoGraph(); $nG->add_resource_triple($uri, $nG->qname_to_uri('rdf:type'), $nG->qname_to_uri('acorn:Foo')); - $result = $mockTripod->saveChanges($oG, $nG, 'http://talisaspire.com/'); + $mockTripod->saveChanges($oG, $nG, 'http://talisaspire.com/'); } - public function testAddMultipleTriplesForSameProperty() + public function testAddMultipleTriplesForSameProperty(): void { $uri = 'http://example.com/resources/1'; @@ -493,6 +501,7 @@ public function testAddMultipleTriplesForSameProperty() $oG->add_literal_triple($uri, $oG->qname_to_uri('dct:title'), 'Some title'); $oG->add_literal_triple($uri, $oG->qname_to_uri('dct:title'), 'Another title'); $oG->add_literal_triple($uri, $oG->qname_to_uri('dct:title'), 'Yet another title'); + $this->tripod->saveChanges(new MongoGraph(), $oG, 'http://talisaspire.com/'); // retrieve it and make sure it was saved correctly @@ -504,7 +513,7 @@ public function testAddMultipleTriplesForSameProperty() $this->assertTrue($g->has_literal_triple($uri, $g->qname_to_uri('dct:title'), 'Yet another title'), 'Graph should contain literal triple we added'); } - public function testRemoveMultipleTriplesForSameProperty() + public function testRemoveMultipleTriplesForSameProperty(): void { $uri = 'http://example.com/resources/1'; @@ -514,11 +523,13 @@ public function testRemoveMultipleTriplesForSameProperty() $oG->add_literal_triple($uri, $oG->qname_to_uri('dct:title'), 'Some title'); $oG->add_literal_triple($uri, $oG->qname_to_uri('dct:title'), 'Another title'); $oG->add_literal_triple($uri, $oG->qname_to_uri('dct:title'), 'Yet another title'); + $this->tripod->saveChanges(new MongoGraph(), $oG, 'http://talisaspire.com/'); // remove all three dct:title triples $g2 = new MongoGraph(); $g2->add_resource_triple($uri, $oG->qname_to_uri('rdf:type'), $oG->qname_to_uri('acorn:Resource')); + $this->tripod->saveChanges($oG, $g2, 'http://talisaspire.com/'); $g = $this->tripod->describeResource($uri); @@ -529,7 +540,7 @@ public function testRemoveMultipleTriplesForSameProperty() $this->assertFalse($g->has_literal_triple($uri, $g->qname_to_uri('dct:title'), 'Yet another title'), 'Graph should not contain literal triple we removed'); } - public function testChangeMultipleTriplesForSamePropertySimple() + public function testChangeMultipleTriplesForSamePropertySimple(): void { $uri = 'http://example.com/resources/1'; @@ -539,6 +550,7 @@ public function testChangeMultipleTriplesForSamePropertySimple() $oG->add_literal_triple($uri, $oG->qname_to_uri('dct:title'), 'Some title'); $oG->add_literal_triple($uri, $oG->qname_to_uri('dct:title'), 'Another title'); $oG->add_literal_triple($uri, $oG->qname_to_uri('dct:title'), 'Yet another title'); + $this->tripod->saveChanges(new MongoGraph(), $oG, 'http://talisaspire.com/'); $g2 = new MongoGraph(); @@ -546,6 +558,7 @@ public function testChangeMultipleTriplesForSamePropertySimple() $g2->add_literal_triple($uri, $g2->qname_to_uri('dct:title'), 'Updated Some title'); $g2->add_literal_triple($uri, $g2->qname_to_uri('dct:title'), 'Updated Another title'); $g2->add_literal_triple($uri, $g2->qname_to_uri('dct:title'), 'Updated Yet another title'); + $this->tripod->saveChanges($oG, $g2, 'http://talisaspire.com/'); $g = $this->tripod->describeResource($uri); @@ -562,7 +575,7 @@ public function testChangeMultipleTriplesForSamePropertySimple() $this->assertTrue($g->has_literal_triple($uri, $g->qname_to_uri('dct:title'), 'Updated Yet another title'), 'Graph should contain literal triple we added'); } - public function testChangeMultipleTriplesForSamePropertyMoreComplex() + public function testChangeMultipleTriplesForSamePropertyMoreComplex(): void { $uri = 'http://example.com/resources/1'; @@ -574,6 +587,7 @@ public function testChangeMultipleTriplesForSamePropertyMoreComplex() $oG->add_literal_triple($uri, $oG->qname_to_uri('dct:title'), 'Title three'); $oG->add_literal_triple($uri, $oG->qname_to_uri('dct:title'), 'Title four'); $oG->add_literal_triple($uri, $oG->qname_to_uri('dct:title'), 'Title five'); + $this->tripod->saveChanges(new MongoGraph(), $oG, 'http://talisaspire.com/'); // new data @@ -583,6 +597,7 @@ public function testChangeMultipleTriplesForSamePropertyMoreComplex() $g2->add_literal_triple($uri, $g2->qname_to_uri('dct:title'), 'New Title two'); $g2->add_literal_triple($uri, $g2->qname_to_uri('dct:title'), 'Title five'); $g2->add_literal_triple($uri, $g2->qname_to_uri('dct:title'), 'New Title seven'); + $this->tripod->saveChanges($oG, $g2, 'http://talisaspire.com/'); $g = $this->tripod->describeResource($uri); @@ -601,7 +616,7 @@ public function testChangeMultipleTriplesForSamePropertyMoreComplex() $this->assertTrue($g->has_literal_triple($uri, $g->qname_to_uri('dct:title'), 'New Title seven'), 'Graph should contain literal triple we added'); } - public function testSetReadPreferenceWhenSavingChanges() + public function testSetReadPreferenceWhenSavingChanges(): void { $subjectOne = 'http://talisaspire.com/works/checkReadPreferencesWrite'; @@ -630,14 +645,15 @@ public function testSetReadPreferenceWhenSavingChanges() $tripodMock ->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdate)); + ->willReturn($tripodUpdate); $g = new MongoGraph(); $g->add_literal_triple($subjectOne, $g->qname_to_uri('dct:title'), 'Title one'); + $tripodMock->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/'); } - public function testReadPreferencesAreRestoredWhenErrorSavingChanges() + public function testReadPreferencesAreRestoredWhenErrorSavingChanges(): void { $subjectOne = 'http://talisaspire.com/works/checkReadPreferencesAreRestoredOnError'; $tripodMock = $this->getMockBuilder(Driver::class) @@ -657,7 +673,7 @@ public function testReadPreferencesAreRestoredWhenErrorSavingChanges() $tripodUpdate ->expects($this->once()) ->method('getContextAlias') - ->will($this->throwException(new Exception('A Test Exception'))); + ->willThrowException(new Exception('A Test Exception')); $tripodUpdate ->expects($this->once()) @@ -666,23 +682,27 @@ public function testReadPreferencesAreRestoredWhenErrorSavingChanges() $tripodMock ->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdate)); + ->willReturn($tripodUpdate); $this->expectException(Exception::class); $g = new MongoGraph(); $g->add_literal_triple($subjectOne, $g->qname_to_uri('dct:title'), 'Title one'); + $tripodMock->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/'); } - public function testReadPreferencesOverMultipleSaves() + public function testReadPreferencesOverMultipleSaves(): void { $subjectOne = 'http://talisaspire.com/works/checkReadPreferencesOverMultipleSaves'; $tripodMock = $this->getMockBuilder(TestTripod::class) ->onlyMethods(['getDataUpdater']) - ->setConstructorArgs(['CBD_testing', 'tripod_php_testing', - ['defaultContext' => 'http://talisaspire.com/', 'readPreference' => ReadPreference::RP_SECONDARY_PREFERRED]]) + ->setConstructorArgs([ + 'CBD_testing', + 'tripod_php_testing', + ['defaultContext' => 'http://talisaspire.com/', 'readPreference' => ReadPreference::RP_SECONDARY_PREFERRED], + ]) ->getMock(); $tripodUpdate = $this->getMockBuilder(Updates::class) @@ -692,47 +712,48 @@ public function testReadPreferencesOverMultipleSaves() $tripodUpdate ->expects($this->exactly(3)) - ->method('validateGraphCardinality') - ->will( - $this->onConsecutiveCalls(null, $this->throwException(new Exception('readPreferenceOverMultipleSavesTestException')), null) - ); + ->method('validateGraphCardinality')->willReturnOnConsecutiveCalls(null, $this->throwException(new Exception('readPreferenceOverMultipleSavesTestException')), null); $tripodMock ->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdate)); + ->willReturn($tripodUpdate); $expectedCollectionReadPreference = $tripodMock->getCollectionReadPreference(); - $this->assertEquals($expectedCollectionReadPreference->getMode(), ReadPreference::RP_SECONDARY_PREFERRED); + $this->assertEquals(ReadPreference::RP_SECONDARY_PREFERRED, $expectedCollectionReadPreference->getMode()); // Assert that a simple save results in read preferences being restored $g = new MongoGraph(); $g->add_literal_triple($subjectOne, $g->qname_to_uri('dct:title'), 'Title one'); + $tripodMock->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/'); $this->assertEquals($expectedCollectionReadPreference, $tripodMock->getCollectionReadPreference()); // Assert a thrown exception still results in read preferences being restored $g = new MongoGraph(); $g->add_literal_triple($subjectOne, $g->qname_to_uri('dct:title2'), 'Title two'); + $exceptionThrown = false; try { $tripodMock->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/'); } catch (Exception $e) { $exceptionThrown = true; - $this->assertEquals('readPreferenceOverMultipleSavesTestException', $e->getMessage()); + $this->assertSame('readPreferenceOverMultipleSavesTestException', $e->getMessage()); } + $this->assertTrue($exceptionThrown); $this->assertEquals($expectedCollectionReadPreference, $tripodMock->getCollectionReadPreference()); // Assert that a new save after the exception still results in read preferences being restored $g = new MongoGraph(); $g->add_literal_triple($subjectOne, $g->qname_to_uri('dct:title3'), 'Title three'); + $tripodMock->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/'); $this->assertEquals($expectedCollectionReadPreference, $tripodMock->getCollectionReadPreference()); } - public function testSaveChangesToLockedDocument() + public function testSaveChangesToLockedDocument(): void { $subjectOne = 'http://talisaspire.com/works/lockedDoc'; @@ -747,7 +768,7 @@ public function testSaveChangesToLockedDocument() $this->tripod->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/'); } - public function testSaveChangesToMultipleSubjects() + public function testSaveChangesToMultipleSubjects(): void { $subjectOne = 'http://example.com/resources/1'; $subjectTwo = 'http://example.com/resources/2'; @@ -760,6 +781,7 @@ public function testSaveChangesToMultipleSubjects() $oG->add_resource_triple($subjectTwo, $oG->qname_to_uri('rdf:type'), $oG->qname_to_uri('acorn:Book')); $oG->add_literal_triple($subjectTwo, $oG->qname_to_uri('dct:title'), 'Title three'); $oG->add_literal_triple($subjectTwo, $oG->qname_to_uri('dct:title'), 'Title four'); + $this->tripod->saveChanges(new MongoGraph(), $oG, 'http://talisaspire.com/'); // retrieve them both, assert they are as we expect @@ -802,7 +824,7 @@ public function testSaveChangesToMultipleSubjects() $this->assertTrue($uG->has_literal_triple($subjectTwo, $uG->qname_to_uri('dct:author'), 'James Brown')); } - public function testDocumentVersioning() + public function testDocumentVersioning(): void { $uri = 'http://example.com/resources/1'; @@ -810,6 +832,7 @@ public function testDocumentVersioning() $g = new MongoGraph(); $g->add_resource_triple($uri, $g->qname_to_uri('rdf:type'), $g->qname_to_uri('acorn:Resource')); $g->add_literal_triple($uri, $g->qname_to_uri('dct:title'), 'wibble'); + $this->tripod->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/', 'something new'); $uG = $this->tripod->describeResource($uri); $this->assertTrue($uG->has_triples_about($uri), 'new entity we created was not saved'); @@ -820,6 +843,7 @@ public function testDocumentVersioning() $nG = new MongoGraph(); $nG->add_graph($g); $nG->add_literal_triple($uri, $g->qname_to_uri('dct:title'), 'another title'); + $this->tripod->saveChanges($g, $nG, 'http://talisaspire.com/'); $uG = $this->tripod->describeResource($uri); $this->assertTrue($uG->has_resource_triple($uri, $g->qname_to_uri('rdf:type'), $g->qname_to_uri('acorn:Resource')), 'Graph should contain resource triple we added'); @@ -830,6 +854,7 @@ public function testDocumentVersioning() $nG = new MongoGraph(); // $nG->add_graph(); $nG->add_literal_triple($uri, $g->qname_to_uri('dct:title'), 'only a title'); + $this->tripod->saveChanges($uG, $nG, 'http://talisaspire.com/'); $uG = $this->tripod->describeResource($uri); @@ -844,7 +869,7 @@ public function testDocumentVersioning() $this->assertDocumentHasBeenDeleted(['r' => $uri, 'c' => 'http://talisaspire.com/']); } - public function testSaveChangesWithInvalidCardinality() + public function testSaveChangesWithInvalidCardinality(): void { $this->expectException(CardinalityException::class); $this->expectExceptionMessage("Cardinality failed on http://foo/bar/1 for 'rdf:type' - should only have 1 value and has: http://foo/bar#Class1, http://foo/bar#Class2"); @@ -890,7 +915,7 @@ public function testSaveChangesWithInvalidCardinality() $tripod->saveChanges($oldGraph, $newGraph, 'http://talisaspire.com/'); } - public function testDiscoverImpactedSubjectsAreDoneAllOperationsASync() + public function testDiscoverImpactedSubjectsAreDoneAllOperationsASync(): void { $uri_1 = 'http://example.com/1'; $uri_2 = 'http://example.com/2'; @@ -966,15 +991,15 @@ public function testDiscoverImpactedSubjectsAreDoneAllOperationsASync() ->method('getComposite'); $mockTripodUpdates->expects($this->once()) ->method('getDiscoverImpactedSubjects') - ->will($this->returnValue($mockDiscoverImpactedSubjects)); + ->willReturn($mockDiscoverImpactedSubjects); $mockTripodUpdates->expects($this->once()) ->method('storeChanges') - ->will($this->returnValue(['subjectsAndPredicatesOfChange' => $subjectsAndPredicatesOfChange, 'transaction_id' => 't1234'])); + ->willReturn(['subjectsAndPredicatesOfChange' => $subjectsAndPredicatesOfChange, 'transaction_id' => 't1234']); $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $mockDiscoverImpactedSubjects->expects($this->once()) ->method('createJob') @@ -986,7 +1011,7 @@ public function testDiscoverImpactedSubjectsAreDoneAllOperationsASync() $mockTripod->saveChanges(new ExtendedGraph(), $oG, 'http://talisaspire.com/'); } - public function testDiscoverImpactedSubjectsForDeletionsSyncOpsAreDoneAsyncJobSubmitted() + public function testDiscoverImpactedSubjectsForDeletionsSyncOpsAreDoneAsyncJobSubmitted(): void { $uri_1 = 'http://example.com/1'; $uri_2 = 'http://example.com/2'; @@ -1108,7 +1133,7 @@ public function testDiscoverImpactedSubjectsForDeletionsSyncOpsAreDoneAsyncJobSu ]; $mockViews->expects($this->once()) ->method('getImpactedSubjects') - ->will($this->returnValue($impactedViewSubjects)); + ->willReturn($impactedViewSubjects); $impactedViewSubjects[0]->expects($this->once())->method('update'); $impactedViewSubjects[1]->expects($this->once())->method('update'); @@ -1150,7 +1175,7 @@ public function testDiscoverImpactedSubjectsForDeletionsSyncOpsAreDoneAsyncJobSu $mockTables->expects($this->once()) ->method('getImpactedSubjects') - ->will($this->returnValue($impactedTableSubjects)); + ->willReturn($impactedTableSubjects); $impactedTableSubjects[0]->expects($this->once())->method('update'); $impactedTableSubjects[1]->expects($this->once())->method('update'); @@ -1159,24 +1184,22 @@ public function testDiscoverImpactedSubjectsForDeletionsSyncOpsAreDoneAsyncJobSu $mockTripodUpdates->expects($this->once()) ->method('getDiscoverImpactedSubjects') - ->will($this->returnValue($mockDiscoverImpactedSubjects)); + ->willReturn($mockDiscoverImpactedSubjects); $mockTripodUpdates->expects($this->once()) ->method('storeChanges') - ->will($this->returnValue(['subjectsAndPredicatesOfChange' => $subjectsAndPredicatesOfChange, 'transaction_id' => 't1234'])); + ->willReturn(['subjectsAndPredicatesOfChange' => $subjectsAndPredicatesOfChange, 'transaction_id' => 't1234']); $mockTripod->expects($this->exactly(2)) ->method('getComposite') - ->will($this->returnValueMap( - [ - [OP_TABLES, $mockTables], - [OP_VIEWS, $mockViews], - ] - )); + ->willReturnMap([ + [OP_TABLES, $mockTables], + [OP_VIEWS, $mockViews], + ]); $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $mockDiscoverImpactedSubjects->expects($this->once()) ->method('createJob') @@ -1188,7 +1211,7 @@ public function testDiscoverImpactedSubjectsForDeletionsSyncOpsAreDoneAsyncJobSu $mockTripod->saveChanges($oG, new ExtendedGraph(), 'http://talisaspire.com/'); } - public function testDiscoverImpactedSubjectsForDefaultOperationsSetting() + public function testDiscoverImpactedSubjectsForDefaultOperationsSetting(): void { $uri_1 = 'http://example.com/1'; $uri_2 = 'http://example.com/2'; @@ -1295,23 +1318,23 @@ public function testDiscoverImpactedSubjectsForDefaultOperationsSetting() $mockTripod->expects($this->once()) ->method('getComposite') ->with(OP_VIEWS) - ->will($this->returnValue($mockViews)); + ->willReturn($mockViews); $mockTripodUpdates->expects($this->once()) ->method('getDiscoverImpactedSubjects') - ->will($this->returnValue($mockDiscoverImpactedSubjects)); + ->willReturn($mockDiscoverImpactedSubjects); $mockTripodUpdates->expects($this->once()) ->method('storeChanges') - ->will($this->returnValue(['subjectsAndPredicatesOfChange' => $subjectsAndPredicatesOfChange, 'transaction_id' => 't1234'])); + ->willReturn(['subjectsAndPredicatesOfChange' => $subjectsAndPredicatesOfChange, 'transaction_id' => 't1234']); $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $mockViews->expects($this->once()) ->method('getImpactedSubjects') - ->will($this->returnValue($impactedViewSubjects)); + ->willReturn($impactedViewSubjects); $impactedViewSubjects[0]->expects($this->once())->method('update'); $impactedViewSubjects[1]->expects($this->once())->method('update'); @@ -1329,7 +1352,7 @@ public function testDiscoverImpactedSubjectsForDefaultOperationsSetting() $mockTripod->saveChanges($oG, $nG, 'http://talisaspire.com/'); } - public function testSpecifyQueueForAsyncOperations() + public function testSpecifyQueueForAsyncOperations(): void { $uri_1 = 'http://example.com/1'; $uri_2 = 'http://example.com/2'; @@ -1440,23 +1463,23 @@ public function testSpecifyQueueForAsyncOperations() $mockTripod->expects($this->once()) ->method('getComposite') ->with(OP_VIEWS) - ->will($this->returnValue($mockViews)); + ->willReturn($mockViews); $mockTripodUpdates->expects($this->once()) ->method('getDiscoverImpactedSubjects') - ->will($this->returnValue($mockDiscoverImpactedSubjects)); + ->willReturn($mockDiscoverImpactedSubjects); $mockTripodUpdates->expects($this->once()) ->method('storeChanges') - ->will($this->returnValue(['subjectsAndPredicatesOfChange' => $subjectsAndPredicatesOfChange, 'transaction_id' => 't1234'])); + ->willReturn(['subjectsAndPredicatesOfChange' => $subjectsAndPredicatesOfChange, 'transaction_id' => 't1234']); $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $mockViews->expects($this->once()) ->method('getImpactedSubjects') - ->will($this->returnValue($impactedViewSubjects)); + ->willReturn($impactedViewSubjects); // This shouldn't be called because ImpactedSubject->update has been mocked and isn't doing anything $mockViews->expects($this->never())->method('update'); @@ -1474,11 +1497,11 @@ public function testSpecifyQueueForAsyncOperations() $mockTripod->saveChanges($oG, $nG, 'http://talisaspire.com/'); } - public function testWriteToUnconfiguredCollectionThrowsException() + public function testWriteToUnconfiguredCollectionThrowsException(): void { // Exception: testing:SOME_COLLECTION is not referenced within config, so cannot be written to $this->expectException(ConfigException::class); - $this->expectExceptionMessage('Collection name \'SOME_COLLECTION\' not in configuration'); + $this->expectExceptionMessage("Collection name 'SOME_COLLECTION' not in configuration"); $tripod = new Driver('SOME_COLLECTION', 'tripod_php_testing'); $tripod->saveChanges(new ExtendedGraph(), new ExtendedGraph(), 'http://talisaspire.com/'); @@ -1491,7 +1514,7 @@ public function testWriteToUnconfiguredCollectionThrowsException() * the cs builder should translate that into a single addition statement and apply it. * This builds on the previous test, by operating on data in mongo where _id.r and _id.c are namespaced. */ - public function testTripodSaveChangesAddsLiteralTripleUsingEmptyOldGraphWithNamespacableIDAndContext() + public function testTripodSaveChangesAddsLiteralTripleUsingEmptyOldGraphWithNamespacableIDAndContext(): void { $oG = new MongoGraph(); $nG = new MongoGraph(); @@ -1510,7 +1533,7 @@ public function testTripodSaveChangesAddsLiteralTripleUsingEmptyOldGraphWithName * the cs builder should translate that into a single addition statement and apply it. * This builds on the previous test, by operating on data in mongo where _id.r and _id.c are namespaced AND passing context into the save method. */ - public function testTripodSaveChangesAddsLiteralTripleUsingEmptyOldGraphWithNamespacedContext() + public function testTripodSaveChangesAddsLiteralTripleUsingEmptyOldGraphWithNamespacedContext(): void { $oG = new MongoGraph(); $nG = new MongoGraph(); @@ -1524,7 +1547,7 @@ public function testTripodSaveChangesAddsLiteralTripleUsingEmptyOldGraphWithName $this->assertHasLiteralTriple($uG, 'http://basedata.com/b/1', $nG->qname_to_uri('searchterms:title'), 'TEST TITLE'); } - public function testDescribeResourceWithNamespace() + public function testDescribeResourceWithNamespace(): void { $noNsG = $this->tripod->describeResource('http://basedata.com/b/1', 'http://basedata.com/b/DefaultGraph'); $nsResourceG = $this->tripod->describeResource('baseData:1', 'http://basedata.com/b/DefaultGraph'); @@ -1541,7 +1564,7 @@ public function testDescribeResourceWithNamespace() $this->assertFalse($nsBothCS->has_changes(), 'Non ns and nsBoth not equal'); } - public function testDescribeResourcesWithNamespace() + public function testDescribeResourcesWithNamespace(): void { $noNsG = $this->tripod->describeResources(['http://basedata.com/b/1'], 'http://basedata.com/b/DefaultGraph'); $nsResourceG = $this->tripod->describeResources(['baseData:1'], 'http://basedata.com/b/DefaultGraph'); @@ -1558,7 +1581,7 @@ public function testDescribeResourcesWithNamespace() $this->assertFalse($nsBothCS->has_changes(), 'Non ns and nsBoth not equal'); } - public function testSelectSingleValueWithNamespaceContextQueryDoesntContainID() + public function testSelectSingleValueWithNamespaceContextQueryDoesntContainID(): void { $expectedResult = [ 'head' => [ @@ -1570,13 +1593,15 @@ public function testSelectSingleValueWithNamespaceContextQueryDoesntContainID() [ '_id' => [ _ID_RESOURCE => 'baseData:1', - _ID_CONTEXT => 'baseData:DefaultGraph'], + _ID_CONTEXT => 'baseData:DefaultGraph', + ], 'rdf:type' => 'acorn:Work', ], [ '_id' => [ _ID_RESOURCE => 'baseData:2', - _ID_CONTEXT => 'baseData:DefaultGraph'], + _ID_CONTEXT => 'baseData:DefaultGraph', + ], 'rdf:type' => ['acorn:Work', 'acorn:Work2'], ], ], @@ -1585,7 +1610,7 @@ public function testSelectSingleValueWithNamespaceContextQueryDoesntContainID() $this->assertEquals($expectedResult, $actualResult); } - public function testSelectSingleValueWithNamespaceContextQueryDoesContainID() + public function testSelectSingleValueWithNamespaceContextQueryDoesContainID(): void { $expectedResult = [ 'head' => [ @@ -1597,7 +1622,8 @@ public function testSelectSingleValueWithNamespaceContextQueryDoesContainID() [ '_id' => [ _ID_RESOURCE => 'baseData:1', - _ID_CONTEXT => 'baseData:DefaultGraph'], + _ID_CONTEXT => 'baseData:DefaultGraph', + ], 'rdf:type' => 'acorn:Work', ], ], @@ -1613,7 +1639,7 @@ public function testSelectSingleValueWithNamespaceContextQueryDoesContainID() $this->assertEquals($expectedResult, $actualResult); } - public function testSelectWithOperandWithNamespaceContextQueryContainsID() + public function testSelectWithOperandWithNamespaceContextQueryContainsID(): void { $expectedResult = [ 'head' => [ @@ -1625,15 +1651,18 @@ public function testSelectWithOperandWithNamespaceContextQueryContainsID() [ '_id' => [ _ID_RESOURCE => 'baseData:1', - _ID_CONTEXT => 'baseData:DefaultGraph'], + _ID_CONTEXT => 'baseData:DefaultGraph', + ], 'rdf:type' => 'acorn:Work', ], [ '_id' => [ _ID_RESOURCE => 'baseData:2', - _ID_CONTEXT => 'baseData:DefaultGraph'], + _ID_CONTEXT => 'baseData:DefaultGraph', + ], 'rdf:type' => ['acorn:Work', 'acorn:Work2'], - ]], + ], + ], ]; $actualResult = $this->tripod->select( ['_id' => ['$in' => [[_ID_RESOURCE => 'baseData:1'], [_ID_RESOURCE => 'baseData:2']]]], @@ -1646,7 +1675,7 @@ public function testSelectWithOperandWithNamespaceContextQueryContainsID() $this->assertEquals($expectedResult, $actualResult); } - public function testSelectWithOperandWithNamespaceContextQueryDoesNotContainID() + public function testSelectWithOperandWithNamespaceContextQueryDoesNotContainID(): void { $expectedResult = [ 'head' => [ @@ -1658,13 +1687,15 @@ public function testSelectWithOperandWithNamespaceContextQueryDoesNotContainID() [ '_id' => [ _ID_RESOURCE => 'baseData:1', - _ID_CONTEXT => 'baseData:DefaultGraph'], + _ID_CONTEXT => 'baseData:DefaultGraph', + ], 'rdf:type' => 'acorn:Work', ], [ '_id' => [ _ID_RESOURCE => 'baseData:2', - _ID_CONTEXT => 'baseData:DefaultGraph'], + _ID_CONTEXT => 'baseData:DefaultGraph', + ], 'rdf:type' => ['acorn:Work', 'acorn:Work2'], ], ], @@ -1680,7 +1711,7 @@ public function testSelectWithOperandWithNamespaceContextQueryDoesNotContainID() $this->assertEquals($expectedResult, $actualResult); } - public function testSelectDocumentWithSpecialFieldTypes() + public function testSelectDocumentWithSpecialFieldTypes(): void { $id = [ 'r' => 'http://talisaspire.com/resources/' . uniqid(), @@ -1707,7 +1738,6 @@ public function testSelectDocumentWithSpecialFieldTypes() // Special field types '_oid' => new ObjectId(), '_bin' => new Binary('foo', Binary::TYPE_OLD_BINARY), - '_fun' => new Javascript('function() { return 42; }'), '_fun' => new Regex('foo', 'i'), ]); @@ -1736,7 +1766,7 @@ public function testSelectDocumentWithSpecialFieldTypes() /** * Return the distinct values of a table column. */ - public function testGetDistinctTableValues() + public function testGetDistinctTableValues(): void { // Get table rows $table = 't_distinct'; @@ -1749,7 +1779,7 @@ public function testGetDistinctTableValues() $this->assertArrayHasKey('count', $results['head']); $this->assertEquals(4, $results['head']['count']); $this->assertArrayHasKey('results', $results); - $this->assertEquals(4, count($results['results'])); + $this->assertCount(4, $results['results']); $this->assertContains('Physics 3rd Edition: Physics for Engineers and Scientists', $results['results']); $this->assertContains('A document title', $results['results']); $this->assertContains('Another document title', $results['results']); @@ -1760,7 +1790,7 @@ public function testGetDistinctTableValues() $this->assertArrayHasKey('count', $results['head']); $this->assertEquals(2, $results['head']['count']); $this->assertArrayHasKey('results', $results); - $this->assertEquals(2, count($results['results'])); + $this->assertCount(2, $results['results']); $this->assertNotContains('Physics 3rd Edition: Physics for Engineers and Scientists', $results['results']); $this->assertContains('A document title', $results['results']); $this->assertContains('Another document title', $results['results']); @@ -1770,7 +1800,7 @@ public function testGetDistinctTableValues() $this->assertArrayHasKey('count', $results['head']); $this->assertEquals(7, $results['head']['count']); $this->assertArrayHasKey('results', $results); - $this->assertEquals(7, count($results['results'])); + $this->assertCount(7, $results['results']); $this->assertContains('acorn:Resource', $results['results']); $this->assertContains('acorn:Work', $results['results']); $this->assertContains('bibo:Book', $results['results']); @@ -1780,19 +1810,19 @@ public function testGetDistinctTableValues() /** * Return no results for tablespec that doesn't exist. */ - public function testDistinctOnTableSpecThatDoesNotExist() + public function testDistinctOnTableSpecThatDoesNotExist(): void { $table = 't_nothing_to_see_here'; $this->expectException(ConfigException::class); - $this->expectExceptionMessage('Table id \'t_nothing_to_see_here\' not in configuration'); - $results = $this->tripod->getDistinctTableColumnValues($table, 'value.foo'); + $this->expectExceptionMessage("Table id 't_nothing_to_see_here' not in configuration"); + $this->tripod->getDistinctTableColumnValues($table, 'value.foo'); } /** * Return no results for distinct on a fieldname that is not defined in tableSpec. */ - public function testDistinctOnFieldNameThatIsNotInTableSpec() + public function testDistinctOnFieldNameThatIsNotInTableSpec(): void { // Get table rows $table = 't_distinct'; @@ -1806,7 +1836,7 @@ public function testDistinctOnFieldNameThatIsNotInTableSpec() /** * Return no results for filters that match no table rows. */ - public function testDistinctForFilterWithNoMatches() + public function testDistinctForFilterWithNoMatches(): void { // Get table rows $table = 't_distinct'; @@ -1818,62 +1848,62 @@ public function testDistinctForFilterWithNoMatches() } /** START: getLockedDocuments tests */ - public function testGetLockedDocuments() + public function testGetLockedDocuments(): void { $subject = 'http://talisaspire.com/works/lockedDoc'; $this->lockDocument($subject, 'transaction_100'); $docs = $this->tripod->getLockedDocuments(); - $this->assertEquals(1, count($docs)); + $this->assertCount(1, $docs); $this->assertEquals($docs[0]['_id']['r'], $subject); - $this->assertEquals($docs[0][_LOCKED_FOR_TRANS], 'transaction_100'); + $this->assertEquals('transaction_100', $docs[0][_LOCKED_FOR_TRANS]); } - public function testGetLockedDocumentsWithFromDateOnly() + public function testGetLockedDocumentsWithFromDateOnly(): void { $subject = 'http://talisaspire.com/works/lockedDoc'; $this->lockDocument($subject, 'transaction_100'); $docs = $this->tripod->getLockedDocuments(date('y-m-d H:i:s', strtotime('+1 min'))); - $this->assertEquals(0, count($docs)); + $this->assertCount(0, $docs); $docs = $this->tripod->getLockedDocuments(date('y-m-d H:i:s', strtotime('-1 min'))); - $this->assertEquals(1, count($docs)); + $this->assertCount(1, $docs); } - public function testGetLockedDocumentsWithTillDateOnly() + public function testGetLockedDocumentsWithTillDateOnly(): void { $subject = 'http://talisaspire.com/works/lockedDoc'; $this->lockDocument($subject, 'transaction_100'); $docs = $this->tripod->getLockedDocuments(null, date('y-m-d H:i:s', strtotime('+1 min'))); - $this->assertEquals(1, count($docs)); + $this->assertCount(1, $docs); $docs = $this->tripod->getLockedDocuments(null, date('y-m-d H:i:s', strtotime('-1 min'))); - $this->assertEquals(0, count($docs)); + $this->assertCount(0, $docs); } - public function testGetLockedDocumentsWithDateRange() + public function testGetLockedDocumentsWithDateRange(): void { $subject = 'http://talisaspire.com/works/lockedDoc'; $this->lockDocument($subject, 'transaction_100'); $docs = $this->tripod->getLockedDocuments(date('y-m-d H:i:s', strtotime('-1 min')), date('y-m-d H:i:s', strtotime('+1 min'))); - $this->assertEquals(1, count($docs)); + $this->assertCount(1, $docs); $docs = $this->tripod->getLockedDocuments(date('y-m-d H:i:s', strtotime('+1 min')), date('y-m-d H:i:s', strtotime('+2 min'))); - $this->assertEquals(0, count($docs)); + $this->assertCount(0, $docs); } /** END: getLockedDocuments tests */ /** START: removeInertLocks tests */ - public function testRemoveInertLocksNoLocksFound() + public function testRemoveInertLocksNoLocksFound(): void { $this->assertFalse($this->tripod->removeInertLocks('transaction_100', 'Unit tests')); } - public function testRemoveInertLocksNotAllLocksAreRemoved() + public function testRemoveInertLocksNotAllLocksAreRemoved(): void { $subjectOne = 'http://talisaspire.com/works/lockedDoc'; $subjectTwo = 'http://basedata.com/b/1'; @@ -1882,14 +1912,14 @@ public function testRemoveInertLocksNotAllLocksAreRemoved() $this->lockDocument($subjectTwo, 'transaction_200'); $docs = $this->tripod->getLockedDocuments(); - $this->assertEquals(2, count($docs)); + $this->assertCount(2, $docs); $this->tripod->removeInertLocks('transaction_200', 'Unit tests'); $docs = $this->tripod->getLockedDocuments(); - $this->assertEquals(1, count($docs)); + $this->assertCount(1, $docs); } - public function testRemoveInertLocksCreateAuditEntryThrowsException() + public function testRemoveInertLocksCreateAuditEntryThrowsException(): void { $subject = 'http://basedata.com/b/1'; $this->lockDocument($subject, 'transaction_400'); @@ -1903,7 +1933,7 @@ public function testRemoveInertLocksCreateAuditEntryThrowsException() ->getMock(); $auditManualRollbackCollection->expects($this->once()) ->method('insertOne') - ->will($this->throwException(new Exception('Some unexpected error occurred.'))); + ->willThrowException(new Exception('Some unexpected error occurred.')); $tripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getDataUpdater']) @@ -1917,16 +1947,16 @@ public function testRemoveInertLocksCreateAuditEntryThrowsException() $tripodUpdate ->expects($this->once()) ->method('getAuditManualRollbacksCollection') - ->will($this->returnValue($auditManualRollbackCollection)); + ->willReturn($auditManualRollbackCollection); $tripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdate)); + ->willReturn($tripodUpdate); $tripod->removeInertLocks('transaction_400', 'Unit tests'); } - public function testRemoveInertLocksUnlockAllDocumentsFailsVerifyErrorEntryInAuditLog() + public function testRemoveInertLocksUnlockAllDocumentsFailsVerifyErrorEntryInAuditLog(): void { $subject = 'http://basedata.com/b/1'; $this->lockDocument($subject, 'transaction_400'); @@ -1949,11 +1979,11 @@ public function testRemoveInertLocksUnlockAllDocumentsFailsVerifyErrorEntryInAud $mockInsert ->expects($this->once()) ->method('isAcknowledged') - ->will($this->returnValue(true)); + ->willReturn(true); $auditManualRollbackCollection->expects($this->once()) ->method('insertOne') - ->will($this->returnValue($mockInsert)); + ->willReturn($mockInsert); $mockUpdate = $this->getMockBuilder(UpdateResult::class) ->disableOriginalConstructor() @@ -1962,12 +1992,12 @@ public function testRemoveInertLocksUnlockAllDocumentsFailsVerifyErrorEntryInAud $mockUpdate ->expects($this->once()) ->method('isAcknowledged') - ->will($this->returnValue(true)); + ->willReturn(true); $auditManualRollbackCollection->expects($this->once()) ->method('updateOne') ->with(['_id' => $mongoDocumentId], ['$set' => ['status' => AUDIT_STATUS_ERROR, _UPDATED_TS => $mongoDate, 'error' => 'Some unexpected error occurred.']]) - ->will($this->returnValue($mockUpdate)); + ->willReturn($mockUpdate); $tripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getDataUpdater']) @@ -1980,28 +2010,28 @@ public function testRemoveInertLocksUnlockAllDocumentsFailsVerifyErrorEntryInAud $tripodUpdate->expects($this->once()) ->method('generateIdForNewMongoDocument') - ->will($this->returnValue($mongoDocumentId)); + ->willReturn($mongoDocumentId); $tripodUpdate->expects($this->exactly(2)) ->method('getMongoDate') - ->will($this->returnValue($mongoDate)); + ->willReturn($mongoDate); $tripodUpdate->expects($this->once()) ->method('getAuditManualRollbacksCollection') - ->will($this->returnValue($auditManualRollbackCollection)); + ->willReturn($auditManualRollbackCollection); $tripodUpdate->expects($this->once()) ->method('unlockAllDocuments') - ->will($this->throwException(new Exception('Some unexpected error occurred.'))); + ->willThrowException(new Exception('Some unexpected error occurred.')); $tripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdate)); + ->willReturn($tripodUpdate); $tripod->removeInertLocks('transaction_400', 'Unit tests'); } - public function testRemoveInertLocksUnlockSuccessfulVerifyAuditLog() + public function testRemoveInertLocksUnlockSuccessfulVerifyAuditLog(): void { $subject = 'http://basedata.com/b/1'; $subject2 = 'tenantData:1'; @@ -2023,7 +2053,7 @@ public function testRemoveInertLocksUnlockSuccessfulVerifyAuditLog() $mockInsert ->expects($this->once()) ->method('isAcknowledged') - ->will($this->returnValue(true)); + ->willReturn(true); $mockUpdate = $this->getMockBuilder(UpdateResult::class) ->disableOriginalConstructor() @@ -2032,7 +2062,7 @@ public function testRemoveInertLocksUnlockSuccessfulVerifyAuditLog() $mockUpdate ->expects($this->once()) ->method('isAcknowledged') - ->will($this->returnValue(true)); + ->willReturn(true); $auditManualRollbackCollection->expects($this->once()) ->method('insertOne') @@ -2045,12 +2075,12 @@ public function testRemoveInertLocksUnlockSuccessfulVerifyAuditLog() 'documents' => ['baseData:1', 'tenantData:1'], _CREATED_TS => $mongoDate, ]) - ->will($this->returnValue($mockInsert)); + ->willReturn($mockInsert); $auditManualRollbackCollection->expects($this->once()) ->method('updateOne') ->with(['_id' => $mongoDocumentId], ['$set' => ['status' => AUDIT_STATUS_COMPLETED, _UPDATED_TS => $mongoDate]]) - ->will($this->returnValue($mockUpdate)); + ->willReturn($mockUpdate); $tripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getDataUpdater']) @@ -2063,38 +2093,38 @@ public function testRemoveInertLocksUnlockSuccessfulVerifyAuditLog() $tripodUpdate->expects($this->once()) ->method('generateIdForNewMongoDocument') - ->will($this->returnValue($mongoDocumentId)); + ->willReturn($mongoDocumentId); $tripodUpdate->expects($this->once()) ->method('getAuditManualRollbacksCollection') - ->will($this->returnValue($auditManualRollbackCollection)); + ->willReturn($auditManualRollbackCollection); $tripodUpdate->expects($this->exactly(2)) ->method('getMongoDate') - ->will($this->returnValue($mongoDate)); + ->willReturn($mongoDate); $tripodUpdate->expects($this->once()) ->method('unlockAllDocuments') - ->will($this->returnValue(true)); + ->willReturn(true); $tripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdate)); + ->willReturn($tripodUpdate); $this->assertTrue($tripod->removeInertLocks('transaction_400', 'Unit tests')); } - public function testRemoveInertLocks() + public function testRemoveInertLocks(): void { $subject = 'http://basedata.com/b/1'; $this->lockDocument($subject, 'transaction_100'); $this->tripod->removeInertLocks('transaction_100', 'Unit tests'); $docs = $this->tripod->getLockedDocuments(); - $this->assertEquals(0, count($docs)); + $this->assertCount(0, $docs); } - public function testStatsD() + public function testStatsD(): void { $mockStatsD = $this->getMockBuilder(StatsD::class) ->onlyMethods(['send']) @@ -2106,9 +2136,9 @@ public function testStatsD() ->onlyMethods(['getStat']) ->setConstructorArgs(['CBD_testing', 'tripod_php_testing', ['defaultContext' => 'http://talisaspire.com/', 'statsDHost' => 'localhost', 'statsDPort' => '2012', 'statsDPrefix' => 'myapp']]) ->getMock(); - $mockTripod->expects($this->any()) + $mockTripod ->method('getStat') - ->will($this->returnValue($mockStatsD)); + ->willReturn($mockStatsD); $mockStatsD->expects($this->once()) ->method('send') @@ -2124,7 +2154,7 @@ public function testStatsD() /** END: removeInertLocks tests */ /** START: saveChangesHooks tests */ - public function testRegisteredHooksAreCalled() + public function testRegisteredHooksAreCalled(): void { $mockHookA = $this->getMockBuilder(TestSaveChangesHookA::class) ->onlyMethods(['pre', 'success', 'failure']) @@ -2148,7 +2178,7 @@ public function testRegisteredHooksAreCalled() $this->tripod->saveChanges(new ExtendedGraph(), new ExtendedGraph()); } - public function testRegisteredSuccessHooksAreNotCalledOnException() + public function testRegisteredSuccessHooksAreNotCalledOnException(): void { $this->expectException(Tripod\Exceptions\Exception::class); $this->expectExceptionMessage('Could not validate'); @@ -2182,7 +2212,7 @@ public function testRegisteredSuccessHooksAreNotCalledOnException() $tripodUpdate->saveChanges(new ExtendedGraph(), new ExtendedGraph()); } - public function testMisbehavingHookDoesNotPreventSaveOrInterfereWithOtherHooks() + public function testMisbehavingHookDoesNotPreventSaveOrInterfereWithOtherHooks(): void { $mockHookA = $this->getMockBuilder(TestSaveChangesHookA::class) ->onlyMethods(['pre', 'success', 'failure']) @@ -2193,8 +2223,8 @@ public function testMisbehavingHookDoesNotPreventSaveOrInterfereWithOtherHooks() ->disableOriginalConstructor() ->getMock(); - $mockHookA->expects($this->once())->method('pre')->will($this->throwException(new Exception('Misbehaving hook'))); - $mockHookA->expects($this->once())->method('success')->will($this->throwException(new Exception('Misbehaving hook'))); + $mockHookA->expects($this->once())->method('pre')->willThrowException(new Exception('Misbehaving hook')); + $mockHookA->expects($this->once())->method('success')->willThrowException(new Exception('Misbehaving hook')); $mockHookA->expects($this->never())->method('failure'); $mockHookB->expects($this->once())->method('pre'); $mockHookB->expects($this->once())->method('success'); @@ -2207,7 +2237,7 @@ public function testMisbehavingHookDoesNotPreventSaveOrInterfereWithOtherHooks() } /** END: saveChangesHooks tests */ - public function testPassStatConfigToTripodConstructor() + public function testPassStatConfigToTripodConstructor(): void { $statsDConfig = $this->getStatsDConfig(); $opts = ['statsConfig' => $statsDConfig]; @@ -2220,8 +2250,7 @@ public function testPassStatConfigToTripodConstructor() $tripod->expects($this->once()) ->method('getStatFromStatFactory') - ->with($opts['statsConfig']) - ->will($this->returnValue($mockStat)); + ->willReturn($mockStat); /** @var StatsD */ $stat = $tripod->getStat(); @@ -2230,7 +2259,7 @@ public function testPassStatConfigToTripodConstructor() $this->assertEquals('example.com', $stat->getHost()); $this->assertEquals(1234, $stat->getPort()); - $this->assertEquals('somePrefix', $stat->getPrefix()); + $this->assertSame('somePrefix', $stat->getPrefix()); $config = $stat->getConfig(); $this->assertEquals( @@ -2254,14 +2283,15 @@ public function testPassStatConfigToTripodConstructor() } /** START: getETag tests */ - public function testEtagIsMicrotimeFormat() + public function testEtagIsMicrotimeFormat(): void { $config = Config::getInstance(); $updatedAt = DateUtil::getMongoDate(); $_id = [ 'r' => 'http://talisaspire.com/resources/testEtag', - 'c' => 'http://talisaspire.com/']; + 'c' => 'http://talisaspire.com/', + ]; $doc = [ '_id' => $_id, 'dct:title' => ['l' => 'etag'], @@ -2275,11 +2305,12 @@ public function testEtagIsMicrotimeFormat() )->insertOne($doc, ['w' => 1]); $tripod = new Driver('CBD_testing', 'tripod_php_testing', ['defaultContext' => 'http://talisaspire.com/']); - $this->assertMatchesRegularExpression('/^0.[0-9]{8} [0-9]{10}/', $tripod->getETag($_id['r'])); + $this->assertMatchesRegularExpression('/^0.\d{8} \d{10}/', $tripod->getETag($_id['r'])); } // END: getETag tests } + class TestSaveChangesHookA implements IEventHook { /** @@ -2288,7 +2319,7 @@ class TestSaveChangesHookA implements IEventHook * * @param $args array of arguments */ - public function pre(array $args) + public function pre(array $args): void { // do nothing } @@ -2300,17 +2331,15 @@ public function pre(array $args) * * @param $args array of arguments */ - public function success(array $args) + public function success(array $args): void { // do nothing } /** * This method gets called if the event failed for any reason. The arguments passed should be the same as IEventHook::pre. - * - * @return mixed */ - public function failure(array $args) + public function failure(array $args): void { // do nothing } @@ -2329,7 +2358,7 @@ class TripodDriverTestConfig extends Tripod\Mongo\Config */ public function __construct() {} - public function loadConfig(array $config) + protected function loadConfig(array $config): void { parent::loadConfig($config); } diff --git a/test/unit/mongo/MongoTripodNQuadSerializerTest.php b/test/unit/mongo/MongoTripodNQuadSerializerTest.php index bbf715a1..84d04f7c 100644 --- a/test/unit/mongo/MongoTripodNQuadSerializerTest.php +++ b/test/unit/mongo/MongoTripodNQuadSerializerTest.php @@ -1,5 +1,7 @@ add_literal_triple('http://example.com/1', $g->qname_to_uri('dct:title'), 'some literal title'); @@ -23,10 +25,10 @@ public function testSerializerSimple() $serializer = new NQuadSerializer(); $actual = $serializer->getSerializedIndex($g->_index, Config::getInstance()->getDefaultContextAlias()); - $this->assertEquals($expected, $actual); + $this->assertSame($expected, $actual); } - public function testSerializerWithMultipleSubjects() + public function testSerializerWithMultipleSubjects(): void { $g = new MongoGraph(); $docs = json_decode(file_get_contents(__DIR__ . '/data/resources.json'), true); @@ -172,7 +174,7 @@ public function testSerializerWithMultipleSubjects() // this test now asserts that each line in $expected has been serialised correctly, without failing // due to new test data. foreach (preg_split("/((\r?\n)|(\r\n?))/", $expected) as $expectedLine) { - $this->assertTrue(strpos($actual, rtrim($expectedLine)) !== false, 'Failed checking for line: ' . rtrim($expectedLine)); + $this->assertStringContainsString(rtrim($expectedLine), $actual, 'Failed checking for line: ' . rtrim($expectedLine)); } } } diff --git a/test/unit/mongo/MongoTripodQueueOperationsTest.php b/test/unit/mongo/MongoTripodQueueOperationsTest.php index ec0ce515..592094ff 100644 --- a/test/unit/mongo/MongoTripodQueueOperationsTest.php +++ b/test/unit/mongo/MongoTripodQueueOperationsTest.php @@ -1,5 +1,7 @@ getMockBuilder(Driver::class) @@ -66,7 +68,7 @@ public function testSingleItemIsAddedToQueueForChangeToSingleSubject() $tripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdates)); + ->willReturn($tripodUpdates); $subjectsAndPredicatesOfChange = [ 'http://talisaspire.com/resources/doc1' => ['dct:subject'], @@ -86,7 +88,7 @@ public function testSingleItemIsAddedToQueueForChangeToSingleSubject() $tripodUpdates->expects($this->once()) ->method('getDiscoverImpactedSubjects') - ->will($this->returnValue($discoverImpactedSubjects)); + ->willReturn($discoverImpactedSubjects); $discoverImpactedSubjects->expects($this->once()) ->method('createJob') @@ -109,7 +111,7 @@ public function testSingleItemIsAddedToQueueForChangeToSingleSubject() /** * Saving a change to a single resource that does not impact any other resources should result in just a single item being added to the queue. */ - public function testSingleItemWithViewsOpIsAddedToQueueForChangeToSingleSubject() + public function testSingleItemWithViewsOpIsAddedToQueueForChangeToSingleSubject(): void { // create a tripod instance that will send all operations to the queue $tripod = $this->getMockBuilder(Driver::class) @@ -141,7 +143,7 @@ public function testSingleItemWithViewsOpIsAddedToQueueForChangeToSingleSubject( $tripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdates)); + ->willReturn($tripodUpdates); $subjectsAndPredicatesOfChange = [ 'http://talisaspire.com/resources/doc1' => ['dct:subject'], @@ -165,7 +167,7 @@ public function testSingleItemWithViewsOpIsAddedToQueueForChangeToSingleSubject( $tripodUpdates->expects($this->once()) ->method('getDiscoverImpactedSubjects') - ->will($this->returnValue($discoverImpactedSubjects)); + ->willReturn($discoverImpactedSubjects); $discoverImpactedSubjects->expects($this->once()) ->method('createJob') @@ -185,7 +187,7 @@ public function testSingleItemWithViewsOpIsAddedToQueueForChangeToSingleSubject( $tripod->saveChanges($g1, $g2); } - public function testNoItemIsAddedToQueueForChangeToSingleSubjectWithNoAsyncOps() + public function testNoItemIsAddedToQueueForChangeToSingleSubjectWithNoAsyncOps(): void { // create a tripod instance that will send all operations to the queue $tripod = $this->getMockBuilder(Driver::class) @@ -213,7 +215,7 @@ public function testNoItemIsAddedToQueueForChangeToSingleSubjectWithNoAsyncOps() $tripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdates)); + ->willReturn($tripodUpdates); $subjectsAndPredicatesOfChange = [ 'http://talisaspire.com/resources/doc1' => ['dct:subject'], @@ -245,7 +247,7 @@ public function testNoItemIsAddedToQueueForChangeToSingleSubjectWithNoAsyncOps() * 4 items being placed on the queue, with the operations for each relevant to the configured operations based on the specifications * todo: new test in composite for one subject that impacts another. */ - public function testSingleJobSubmittedToQueueForChangeToSeveralSubjects() + public function testSingleJobSubmittedToQueueForChangeToSeveralSubjects(): void { $tripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getDataUpdater', 'getComposite']) @@ -276,7 +278,7 @@ public function testSingleJobSubmittedToQueueForChangeToSeveralSubjects() $tripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdates)); + ->willReturn($tripodUpdates); $subjectsAndPredicatesOfChange = [ 'http://talisaspire.com/resources/doc1' => ['dct:date'], @@ -298,7 +300,7 @@ public function testSingleJobSubmittedToQueueForChangeToSeveralSubjects() $tripodUpdates->expects($this->once()) ->method('getDiscoverImpactedSubjects') - ->will($this->returnValue($discoverImpactedSubjects)); + ->willReturn($discoverImpactedSubjects); $discoverImpactedSubjects->expects($this->once()) ->method('createJob') diff --git a/test/unit/mongo/MongoTripodSearchDocumentsTest.php b/test/unit/mongo/MongoTripodSearchDocumentsTest.php index 6ff3fcc2..ba436de5 100644 --- a/test/unit/mongo/MongoTripodSearchDocumentsTest.php +++ b/test/unit/mongo/MongoTripodSearchDocumentsTest.php @@ -1,5 +1,7 @@ expectException(Exception::class); $this->expectExceptionMessage('Resource must be specified'); @@ -37,7 +39,7 @@ public function testGenerateSearchDocumentBasedOnSpecIdThrowsExceptionWithEmptyR $searchDocuments->generateSearchDocumentBasedOnSpecId('i_search_resource', null, 'http://talisaspire.com/'); } - public function testGenerateSearchDocumentBasedOnSpecIdThrowsExceptionWithEmptyContext() + public function testGenerateSearchDocumentBasedOnSpecIdThrowsExceptionWithEmptyContext(): void { $this->expectException(Exception::class); $this->expectExceptionMessage('Context must be specified'); @@ -45,7 +47,7 @@ public function testGenerateSearchDocumentBasedOnSpecIdThrowsExceptionWithEmptyC $searchDocuments->generateSearchDocumentBasedOnSpecId('i_search_resource', 'http://talisaspire.com/resource/1', null); } - public function testGenerateSearchDocumentBasedOnSpecIdReturnNullForInvalidSearchSpecId() + public function testGenerateSearchDocumentBasedOnSpecIdReturnNullForInvalidSearchSpecId(): void { $mockSearchDocuments = $this->getMockBuilder(SearchDocuments::class) ->onlyMethods(['getSearchDocumentSpecification']) @@ -54,26 +56,26 @@ public function testGenerateSearchDocumentBasedOnSpecIdReturnNullForInvalidSearc $mockSearchDocuments->expects($this->once()) ->method('getSearchDocumentSpecification') - ->will($this->returnValue(null)); + ->willReturn(null); $generatedDocuments = $mockSearchDocuments->generateSearchDocumentBasedOnSpecId('i_search_something', 'http://talisaspire.com/resource/1', 'http://talisaspire.com/'); $this->assertNull($generatedDocuments); } - public function testGenerateSearchDocumentBasedOnSpecIdReturnNullIfNoMatchForResourceFound() + public function testGenerateSearchDocumentBasedOnSpecIdReturnNullIfNoMatchForResourceFound(): void { $searchDocuments = $this->getSearchDocuments($this->tripod); $generatedDocuments = $searchDocuments->generateSearchDocumentBasedOnSpecId('i_search_resource', 'http://talisaspire.com/resource/1', 'http://talisaspire.com/'); $this->assertNull($generatedDocuments); } - public function testGenerateSearchDocumentBasedOnSpecId() + public function testGenerateSearchDocumentBasedOnSpecId(): void { $searchDocuments = $this->getSearchDocuments($this->tripod); $generatedDocuments = $searchDocuments->generateSearchDocumentBasedOnSpecId('i_search_resource', 'http://talisaspire.com/resources/doc1', 'http://talisaspire.com/'); $this->assertEquals('http://talisaspire.com/resources/doc1', $generatedDocuments['_id']['r']); } - public function testGenerateSearchDocumentPreservesDiacritics() + public function testGenerateSearchDocumentPreservesDiacritics(): void { $searchDocuments = $this->getSearchDocuments($this->tripod); $generatedDocuments = $searchDocuments->generateSearchDocumentBasedOnSpecId('i_search_resource', 'http://talisaspire.com/resources/doc13', 'http://talisaspire.com/'); @@ -82,9 +84,12 @@ public function testGenerateSearchDocumentPreservesDiacritics() $this->assertEquals('http://talisaspire.com/resources/doc13', $generatedDocuments['_id']['r']); } - public function testGenerateSearchDocumentBasedOnSpecIdWithFieldNamePredicatesHavingNoValueInCollection() + public function testGenerateSearchDocumentBasedOnSpecIdWithFieldNamePredicatesHavingNoValueInCollection(): void { - $searchSpecs = json_decode('{"_id":"i_search_resource","type":["bibo:Book"],"from":"CBD_testing","filter":[{"condition":{"dct:title.l":{"$exists":true}}}],"indices":[{"fieldName":"search_terms","predicates":["dct:title","dct:subject"]},{"fieldName":"other_terms","predicates":["rdf:type"]}],"fields":[{"fieldName":"result.title","predicates":["dct:title"],"limit":1},{"fieldName":"result.link","value":"link"},{"fieldName":"rdftype","predicates":["rdf:type"],"limit":1}],"joins":{"dct:creator":{"indices":[{"fieldName":"search_terms","predicates":["foaf:name"]}],"fields":[{"fieldName":"result.author","predicates":["foaf:name"],"limit":1}, {"fieldName":"result.role","predicates":["siocAccess:Role"], "limit":1}] } }}', true); + $searchSpecs = json_decode( + '{"_id":"i_search_resource","type":["bibo:Book"],"from":"CBD_testing","filter":[{"condition":{"dct:title.l":{"$exists":true}}}],"indices":[{"fieldName":"search_terms","predicates":["dct:title","dct:subject"]},{"fieldName":"other_terms","predicates":["rdf:type"]}],"fields":[{"fieldName":"result.title","predicates":["dct:title"],"limit":1},{"fieldName":"result.link","value":"link"},{"fieldName":"rdftype","predicates":["rdf:type"],"limit":1}],"joins":{"dct:creator":{"indices":[{"fieldName":"search_terms","predicates":["foaf:name"]}],"fields":[{"fieldName":"result.author","predicates":["foaf:name"],"limit":1}, {"fieldName":"result.role","predicates":["siocAccess:Role"], "limit":1}] } }}', + true + ); $mockSearchDocuments = $this->getMockBuilder(SearchDocuments::class) ->onlyMethods(['getSearchDocumentSpecification']) @@ -93,14 +98,14 @@ public function testGenerateSearchDocumentBasedOnSpecIdWithFieldNamePredicatesHa $mockSearchDocuments->expects($this->once()) ->method('getSearchDocumentSpecification') - ->will($this->returnValue($searchSpecs)); + ->willReturn($searchSpecs); $generatedDocuments = $mockSearchDocuments->generateSearchDocumentBasedOnSpecId('i_search_resource', 'http://talisaspire.com/resources/doc1', 'http://talisaspire.com/'); $this->assertNotNull($generatedDocuments); $this->assertEquals('http://talisaspire.com/resources/doc1', $generatedDocuments['_id']['r']); } - public function testSearchDocumentsGenerateWhenDefinedPredicateChanges() + public function testSearchDocumentsGenerateWhenDefinedPredicateChanges(): void { $uri = 'http://talisaspire.com/resources/doc1'; @@ -128,7 +133,7 @@ public function testSearchDocumentsGenerateWhenDefinedPredicateChanges() $searchIndexer->expects($this->once()) ->method('getSearchDocumentGenerator') - ->will($this->returnValue($searchDocuments)); + ->willReturn($searchDocuments); $searchDocuments->expects($this->once()) ->method('generateSearchDocumentBasedOnSpecId') @@ -156,7 +161,7 @@ public function testSearchDocumentsGenerateWhenDefinedPredicateChanges() } } - public function testSearchDocsShouldRegenerateWhenUndefinedPredicateChangesButFilterExistsInSpec() + public function testSearchDocsShouldRegenerateWhenUndefinedPredicateChangesButFilterExistsInSpec(): void { $uri = 'http://talisaspire.com/resources/doc1'; @@ -180,7 +185,7 @@ public function testSearchDocsShouldRegenerateWhenUndefinedPredicateChangesButFi $this->assertEmpty($impactedSubjects[0]->getSpecTypes()); } - public function testUpdateOfResourceInImpactIndexTriggersRegenerationOfSearchDocs() + public function testUpdateOfResourceInImpactIndexTriggersRegenerationOfSearchDocs(): void { $uri = 'http://talisaspire.com/authors/2'; $labeller = new Labeller(); @@ -212,7 +217,7 @@ public function testUpdateOfResourceInImpactIndexTriggersRegenerationOfSearchDoc $searchIndexer->expects($this->once()) ->method('getSearchDocumentGenerator') - ->will($this->returnValue($searchDocuments)); + ->willReturn($searchDocuments); $searchDocuments->expects($this->once()) ->method('generateSearchDocumentBasedOnSpecId') @@ -240,7 +245,7 @@ public function testUpdateOfResourceInImpactIndexTriggersRegenerationOfSearchDoc } } - public function testRdfTypeTriggersGenerationOfSearchDocuments() + public function testRdfTypeTriggersGenerationOfSearchDocuments(): void { $uri = 'http://example.com/resources/' . uniqid(); @@ -252,7 +257,8 @@ public function testRdfTypeTriggersGenerationOfSearchDocuments() $subjectsAndPredicatesOfChange = [ $labeller->uri_to_alias($uri) => [ - 'rdf:type', 'bibo:issn', + 'rdf:type', + 'bibo:issn', ], ]; @@ -293,7 +299,7 @@ public function testRdfTypeTriggersGenerationOfSearchDocuments() $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $mockTripodUpdates->expects($this->once()) ->method('processSyncOperations') @@ -326,7 +332,7 @@ public function testRdfTypeTriggersGenerationOfSearchDocuments() $searchIndexer->expects($this->once()) ->method('getSearchDocumentGenerator') - ->will($this->returnValue($searchDocuments)); + ->willReturn($searchDocuments); $searchDocuments->expects($this->once()) ->method('generateSearchDocumentBasedOnSpecId') @@ -356,7 +362,7 @@ public function testRdfTypeTriggersGenerationOfSearchDocuments() } } - public function testNewResourceThatDoesNotMatchAnythingCreatesNoImpactedSubjects() + public function testNewResourceThatDoesNotMatchAnythingCreatesNoImpactedSubjects(): void { $uri = 'http://example.com/resources/' . uniqid(); $labeller = new Labeller(); @@ -405,7 +411,7 @@ public function testNewResourceThatDoesNotMatchAnythingCreatesNoImpactedSubjects $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $mockTripodUpdates->expects($this->once()) ->method('processSyncOperations') @@ -428,7 +434,7 @@ public function testNewResourceThatDoesNotMatchAnythingCreatesNoImpactedSubjects $this->assertEmpty($searchIndexer->getImpactedSubjects($subjectsAndPredicatesOfChange, $this->defaultContext)); } - public function testDeleteResourceCreatesImpactedSubjects() + public function testDeleteResourceCreatesImpactedSubjects(): void { $uri = 'http://example.com/resources/' . uniqid(); $labeller = new Labeller(); @@ -586,7 +592,7 @@ public function testDeleteResourceCreatesImpactedSubjects() $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $expectedSubjectsAndPredicatesOfChange = [ $creatorUriAlias => ['rdf:type', 'foaf:name'], @@ -664,10 +670,7 @@ public function testDeleteResourceCreatesImpactedSubjects() $this->assertEquals(0, $collection->count($impactQuery)); } - /** - * @return SearchDocuments - */ - protected function getSearchDocuments(Driver $tripod) + protected function getSearchDocuments(Driver $tripod): SearchDocuments { return new SearchDocuments( $tripod->getStoreName(), diff --git a/test/unit/mongo/MongoTripodSearchIndexerTest.php b/test/unit/mongo/MongoTripodSearchIndexerTest.php index a2cb922f..c8e444c5 100644 --- a/test/unit/mongo/MongoTripodSearchIndexerTest.php +++ b/test/unit/mongo/MongoTripodSearchIndexerTest.php @@ -1,5 +1,7 @@ getCollectionsForSearch($this->tripod->getStoreName()) as $collection) { $collection->drop(); } + $this->loadResourceDataViaTripod(); $this->loadBaseSearchDataViaTripod(); } - public function testSearchDocumentsRegenerateWhenDefinedPredicateChanged() + public function testSearchDocumentsRegenerateWhenDefinedPredicateChanged(): void { // First make a change that affects a search document $tripod = $this->getMockBuilder(Driver::class) @@ -68,11 +71,11 @@ public function testSearchDocumentsRegenerateWhenDefinedPredicateChanged() $tripodUpdate->expects($this->atLeastOnce()) ->method('storeChanges') - ->will($this->returnValue(['subjectsAndPredicatesOfChange' => $subjectsAndPredicatesOfChange, 'transaction_id' => 't1234'])); + ->willReturn(['subjectsAndPredicatesOfChange' => $subjectsAndPredicatesOfChange, 'transaction_id' => 't1234']); $tripod->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdate)); + ->willReturn($tripodUpdate); $searchIndexer = $this->getMockBuilder(SearchIndexer::class) ->onlyMethods(['getSearchProvider', 'getImpactedSubjects']) @@ -89,7 +92,7 @@ public function testSearchDocumentsRegenerateWhenDefinedPredicateChanged() ->with( $this->matchesRegularExpression('/http:\/\/talisaspire\.com\/resources\/doc(1|2|3)$/'), 'http://talisaspire.com/', - $this->equalTo(['i_search_resource']) + ['i_search_resource'] ); $searchProvider->expects($this->exactly(3)) @@ -97,7 +100,7 @@ public function testSearchDocumentsRegenerateWhenDefinedPredicateChanged() $searchIndexer->expects($this->atLeastOnce()) ->method('getSearchProvider') - ->will($this->returnValue($searchProvider)); + ->willReturn($searchProvider); $impactedSubjects = [ $this->getMockBuilder(ImpactedSubject::class) @@ -147,18 +150,18 @@ public function testSearchDocumentsRegenerateWhenDefinedPredicateChanged() ->getMock(), ]; - $impactedSubjects[0]->expects($this->once())->method('getTripod')->will($this->returnValue($tripod)); - $impactedSubjects[1]->expects($this->once())->method('getTripod')->will($this->returnValue($tripod)); - $impactedSubjects[2]->expects($this->once())->method('getTripod')->will($this->returnValue($tripod)); + $impactedSubjects[0]->expects($this->once())->method('getTripod')->willReturn($tripod); + $impactedSubjects[1]->expects($this->once())->method('getTripod')->willReturn($tripod); + $impactedSubjects[2]->expects($this->once())->method('getTripod')->willReturn($tripod); $searchIndexer->expects($this->once()) ->method('getImpactedSubjects') ->with($subjectsAndPredicatesOfChange, 'http://talisaspire.com/') - ->will($this->returnValue($impactedSubjects)); + ->willReturn($impactedSubjects); $tripod->expects($this->atLeastOnce()) ->method('getSearchIndexer') - ->will($this->returnValue($searchIndexer)); + ->willReturn($searchIndexer); $g1 = $tripod->describeResource('http://talisaspire.com/authors/1'); $g2 = $tripod->describeResource('http://talisaspire.com/authors/1'); @@ -196,7 +199,7 @@ public function testSearchDocumentsRegenerateWhenDefinedPredicateChanged() $searchProvider->expects($this->exactly(1)) ->method('deleteDocument') ->with( - $this->equalTo('http://talisaspire.com/lists/1234'), + 'http://talisaspire.com/lists/1234', 'http://talisaspire.com/', $this->isEmpty() ); @@ -206,7 +209,7 @@ public function testSearchDocumentsRegenerateWhenDefinedPredicateChanged() $searchIndexer->expects($this->atLeastOnce()) ->method('getSearchProvider') - ->will($this->returnValue($searchProvider)); + ->willReturn($searchProvider); $impactedSubject = $this->getMockBuilder(ImpactedSubject::class) ->setConstructorArgs( @@ -223,13 +226,13 @@ public function testSearchDocumentsRegenerateWhenDefinedPredicateChanged() ->onlyMethods(['getTripod']) ->getMock(); - $impactedSubject->expects($this->once())->method('getTripod')->will($this->returnValue($tripod)); + $impactedSubject->expects($this->once())->method('getTripod')->willReturn($tripod); - $searchIndexer->expects($this->once())->method('getImpactedSubjects')->will($this->returnValue([$impactedSubject])); + $searchIndexer->expects($this->once())->method('getImpactedSubjects')->willReturn([$impactedSubject]); $tripod->expects($this->atLeastOnce()) ->method('getSearchIndexer') - ->will($this->returnValue($searchIndexer)); + ->willReturn($searchIndexer); $list = new ExtendedGraph(); $list->add_resource_triple('http://talisaspire.com/lists/1234', RDF_TYPE, 'http://purl.org/vocab/resourcelist/schema#List'); @@ -287,12 +290,12 @@ public function testSearchDocumentsRegenerateWhenDefinedPredicateChanged() ->onlyMethods(['getTripod']) ->getMock(); - $impactedSubject->expects($this->once())->method('getTripod')->will($this->returnValue($tripod)); + $impactedSubject->expects($this->once())->method('getTripod')->willReturn($tripod); $searchProvider->expects($this->exactly(1)) ->method('deleteDocument') ->with( - $this->equalTo('http://talisaspire.com/lists/1234'), + 'http://talisaspire.com/lists/1234', 'http://talisaspire.com/', ['i_search_list'] ); @@ -302,13 +305,13 @@ public function testSearchDocumentsRegenerateWhenDefinedPredicateChanged() $searchIndexer->expects($this->atLeastOnce()) ->method('getSearchProvider') - ->will($this->returnValue($searchProvider)); + ->willReturn($searchProvider); - $searchIndexer->expects($this->once())->method('getImpactedSubjects')->will($this->returnValue([$impactedSubject])); + $searchIndexer->expects($this->once())->method('getImpactedSubjects')->willReturn([$impactedSubject]); $tripod->expects($this->atLeastOnce()) ->method('getSearchIndexer') - ->will($this->returnValue($searchIndexer)); + ->willReturn($searchIndexer); $oldList = $tripod->describeResource('http://talisaspire.com/lists/1234'); $list = $tripod->describeResource('http://talisaspire.com/lists/1234'); @@ -325,7 +328,7 @@ public function testSearchDocumentsRegenerateWhenDefinedPredicateChanged() ); } - public function testSearchDocumentsNotRegeneratedIfChangeIsNotInSearchSpec() + public function testSearchDocumentsNotRegeneratedIfChangeIsNotInSearchSpec(): void { // Now make a change that shouldn't affect any search docs $tripod = $this->getMockBuilder(Driver::class) @@ -360,11 +363,11 @@ public function testSearchDocumentsNotRegeneratedIfChangeIsNotInSearchSpec() ->getMock(); $tripodUpdate->expects($this->atLeastOnce()) ->method('storeChanges') - ->will($this->returnValue(['deletedSubjects' => [], 'subjectsAndPredicatesOfChange' => [], 'transaction_id' => 't1234'])); + ->willReturn(['deletedSubjects' => [], 'subjectsAndPredicatesOfChange' => [], 'transaction_id' => 't1234']); $tripod->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdate)); + ->willReturn($tripodUpdate); $searchIndexer = $this->getMockBuilder(SearchIndexer::class) ->onlyMethods(['getSearchProvider', 'update']) @@ -382,20 +385,21 @@ public function testSearchDocumentsNotRegeneratedIfChangeIsNotInSearchSpec() $searchProvider->expects($this->never()) ->method('indexDocument'); - $searchIndexer->expects($this->any()) + $searchIndexer ->method('getSearchProvider') - ->will($this->returnValue($searchProvider)); + ->willReturn($searchProvider); $searchIndexer->expects($this->never()) ->method('update'); $tripod->expects($this->atLeastOnce()) ->method('getSearchIndexer') - ->will($this->returnValue($searchIndexer)); + ->willReturn($searchIndexer); $g1 = $tripod->describeResource('http://talisaspire.com/authors/1'); $g2 = $tripod->describeResource('http://talisaspire.com/authors/1'); $g2->add_literal_triple('http://talisaspire.com/authors/1', $g2->qname_to_uri('foaf:dob'), '1564-04-26'); + $tripod->saveChanges($g1, $g2); } @@ -403,7 +407,7 @@ public function testSearchDocumentsNotRegeneratedIfChangeIsNotInSearchSpec() * Save several new resources in a single operation. Only one of the resources has a type that is applicable based on specifications, * therefore only one ImpactedSubject should be created. */ - public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject() + public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject(): void { $tripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getDataUpdater']) @@ -440,7 +444,7 @@ public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject() $tripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdates)); + ->willReturn($tripodUpdates); // first lets add a book, which should trigger a search doc, view and table gen for a single item $g = new MongoGraph(); @@ -466,6 +470,7 @@ public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject() $g->add_literal_triple($newSubjectUri3, $g->qname_to_uri('dct:title'), 'This is yet another new resource'); $g->add_literal_triple($newSubjectUri3, $g->qname_to_uri('dct:subject'), 'art'); $g->add_literal_triple($newSubjectUri3, $g->qname_to_uri('dct:subject'), 'design'); + $subjectsAndPredicatesOfChange = [ $newSubjectUri1 => ['rdf:type', 'dct:creator', 'dct:title', 'dct:subject'], $newSubjectUri2 => ['rdf:type', 'dct:creator', 'dct:title', 'dct:subject'], @@ -492,7 +497,7 @@ public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject() $this->assertEquals($expectedImpactedSubjects, $impactedSubjects); } - public function testBatchSearchDocumentsGeneration() + public function testBatchSearchDocumentsGeneration(): void { $count = 234; $docs = []; @@ -526,10 +531,10 @@ public function testBatchSearchDocumentsGeneration() $configInstance->expects($this->atLeastOnce())->method('getCollectionForCBD')->willReturn($collection); $tripod = $this->getMockBuilder(Driver::class) - ->onlyMethods(['getConfigInstance']) - ->setConstructorArgs(['tripod_php_testing', 'CBD_testing']) - ->disableOriginalConstructor() + ->onlyMethods(['getStoreName']) + ->setConstructorArgs(['CBD_testing', 'tripod_php_testing']) ->getMock(); + $tripod->expects($this->atLeastOnce())->method('getStoreName')->willReturn('tripod_php_testing'); $search = $this->getMockBuilder(SearchIndexer::class) ->onlyMethods(['setSearchProvider', 'getConfigInstance', 'queueApplyJob', 'getJobGroup']) diff --git a/test/unit/mongo/MongoTripodStatTest.php b/test/unit/mongo/MongoTripodStatTest.php index 457b8dfc..3c703ac4 100644 --- a/test/unit/mongo/MongoTripodStatTest.php +++ b/test/unit/mongo/MongoTripodStatTest.php @@ -1,12 +1,14 @@ getStatsDConfig(); @@ -15,28 +17,28 @@ public function testStatFactory() $this->assertInstanceOf(StatsD::class, $stat); $this->assertEquals('example.com', $stat->getHost()); $this->assertEquals(1234, $stat->getPort()); - $this->assertEquals('somePrefix', $stat->getPrefix()); + $this->assertSame('somePrefix', $stat->getPrefix()); $noStat = TripodStatFactory::create(); $this->assertInstanceOf(NoStat::class, $noStat); } - public function testStatsDSettersAndGetters() + public function testStatsDSettersAndGetters(): void { $stat = StatsD::createFromConfig($this->getStatsDConfig()); $this->assertInstanceOf(StatsD::class, $stat); $this->assertEquals('example.com', $stat->getHost()); $this->assertEquals(1234, $stat->getPort()); - $this->assertEquals('somePrefix', $stat->getPrefix()); + $this->assertSame('somePrefix', $stat->getPrefix()); $this->assertEquals($this->getStatsDConfig(), $stat->getConfig()); $stat = new StatsD('foo.bar', 9876); $this->assertEquals('foo.bar', $stat->getHost()); $this->assertEquals(9876, $stat->getPort()); - $this->assertEquals('', $stat->getPrefix()); - $this->assertEquals(['class' => StatsD::class, 'config' => ['host' => 'foo.bar', 'port' => 9876, 'prefix' => '']], $stat->getConfig()); + $this->assertSame('', $stat->getPrefix()); + $this->assertSame(['class' => StatsD::class, 'config' => ['host' => 'foo.bar', 'port' => 9876, 'prefix' => '']], $stat->getConfig()); $stat->setHost('bar.baz'); $this->assertEquals('bar.baz', $stat->getHost()); @@ -44,12 +46,12 @@ public function testStatsDSettersAndGetters() $stat->setPort(4567); $this->assertEquals(4567, $stat->getPort()); $stat->setPrefix('FOO_BAR'); - $this->assertEquals('FOO_BAR', $stat->getPrefix()); + $this->assertSame('FOO_BAR', $stat->getPrefix()); - $this->assertEquals(['class' => StatsD::class, 'config' => ['host' => 'bar.baz', 'port' => 4567, 'prefix' => 'FOO_BAR']], $stat->getConfig()); + $this->assertSame(['class' => StatsD::class, 'config' => ['host' => 'bar.baz', 'port' => 4567, 'prefix' => 'FOO_BAR']], $stat->getConfig()); } - public function testStatsDIncrementNoPrefix() + public function testStatsDIncrementNoPrefix(): void { $statConfig = $this->getStatsDConfig(); @@ -64,7 +66,7 @@ public function testStatsDIncrementNoPrefix() $stat->increment('FOO.BAR'); } - public function testStatsDIncrementWithPivotValueNoPrefix() + public function testStatsDIncrementWithPivotValueNoPrefix(): void { $statConfig = $this->getStatsDConfig(); @@ -84,7 +86,7 @@ public function testStatsDIncrementWithPivotValueNoPrefix() $stat->increment('FOO.BAR'); } - public function testStatsDIncrementWithPrefix() + public function testStatsDIncrementWithPrefix(): void { $statConfig = $this->getStatsDConfig(); @@ -99,7 +101,7 @@ public function testStatsDIncrementWithPrefix() $stat->increment('FOO.BAR'); } - public function testStatsDIncrementWithPivotValueAndPrefix() + public function testStatsDIncrementWithPivotValueAndPrefix(): void { $statConfig = $this->getStatsDConfig(); @@ -117,7 +119,7 @@ public function testStatsDIncrementWithPivotValueAndPrefix() $stat->increment('FOO.BAR', 5); } - public function testStatsDTimerNoPrefix() + public function testStatsDTimerNoPrefix(): void { $statConfig = $this->getStatsDConfig(); @@ -132,7 +134,7 @@ public function testStatsDTimerNoPrefix() $stat->timer('FOO.BAR', 1234); } - public function testStatsDTimerWithPivotValueNoPrefix() + public function testStatsDTimerWithPivotValueNoPrefix(): void { $statConfig = $this->getStatsDConfig(); @@ -150,7 +152,7 @@ public function testStatsDTimerWithPivotValueNoPrefix() $stat->timer('FOO.BAR', 1234); } - public function testStatsDTimerWithPrefix() + public function testStatsDTimerWithPrefix(): void { $statConfig = $this->getStatsDConfig(); @@ -165,7 +167,7 @@ public function testStatsDTimerWithPrefix() $stat->timer('FOO.BAR', 4567); } - public function testStatsDTimerWithPrefixAndPivotValue() + public function testStatsDTimerWithPrefixAndPivotValue(): void { $statConfig = $this->getStatsDConfig(); @@ -183,7 +185,7 @@ public function testStatsDTimerWithPrefixAndPivotValue() $stat->timer('FOO.BAR', 4567); } - public function testStatsDGaugeNoPrefix() + public function testStatsDGaugeNoPrefix(): void { $statConfig = $this->getStatsDConfig(); @@ -198,7 +200,7 @@ public function testStatsDGaugeNoPrefix() $stat->gauge('FOO.BAR', 'xyz'); } - public function testStatsDGaugeWithPivotValueNoPrefix() + public function testStatsDGaugeWithPivotValueNoPrefix(): void { $statConfig = $this->getStatsDConfig(); @@ -216,7 +218,7 @@ public function testStatsDGaugeWithPivotValueNoPrefix() $stat->gauge('FOO.BAR', 'xyz'); } - public function testStatsDGaugeWithPrefix() + public function testStatsDGaugeWithPrefix(): void { $statConfig = $this->getStatsDConfig(); @@ -231,7 +233,7 @@ public function testStatsDGaugeWithPrefix() $stat->gauge('FOO.BAR', 'abc'); } - public function testStatsDGaugeWithPrefixAndPivotValue() + public function testStatsDGaugeWithPrefixAndPivotValue(): void { $statConfig = $this->getStatsDConfig(); @@ -249,31 +251,31 @@ public function testStatsDGaugeWithPrefixAndPivotValue() $stat->gauge('FOO.BAR', 'abc'); } - public function testPrefixCannotStartWithDot() + public function testPrefixCannotStartWithDot(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid prefix supplied'); - $stat = new StatsD('foo.bar', 4567, '.some_prefix'); + new StatsD('foo.bar', 4567, '.some_prefix'); } - public function testPrefixCannotEndWithDot() + public function testPrefixCannotEndWithDot(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid prefix supplied'); - $stat = new StatsD('foo.bar', 4567, 'some_prefix.'); + new StatsD('foo.bar', 4567, 'some_prefix.'); } - public function testPrefixCannotContainConsecutiveDot() + public function testPrefixCannotContainConsecutiveDot(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid prefix supplied'); - $stat = new StatsD('foo.bar', 4567, 'some..prefix'); + new StatsD('foo.bar', 4567, 'some..prefix'); } - public function testPivotValueCannotStartWithDot() + public function testPivotValueCannotStartWithDot(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid pivot value supplied'); @@ -282,7 +284,7 @@ public function testPivotValueCannotStartWithDot() $stat->setPivotValue('.someValue'); } - public function testPivotValueCannotEndWithDot() + public function testPivotValueCannotEndWithDot(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid pivot value supplied'); @@ -291,7 +293,7 @@ public function testPivotValueCannotEndWithDot() $stat->setPivotValue('someValue.'); } - public function testPivotValueCannotContainConsecutiveDot() + public function testPivotValueCannotContainConsecutiveDot(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid pivot value supplied'); diff --git a/test/unit/mongo/MongoTripodTablesTest.php b/test/unit/mongo/MongoTripodTablesTest.php index 0ad9a914..4467da83 100644 --- a/test/unit/mongo/MongoTripodTablesTest.php +++ b/test/unit/mongo/MongoTripodTablesTest.php @@ -1,5 +1,7 @@ tripodTables->generateTableRows('t_resource', 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA-2'); @@ -89,6 +91,7 @@ public function testTripodSaveChangesUpdatesLiteralTripleInTable() $g2 = $this->tripod->describeResource('http://talisaspire.com/works/4d101f63c10a6'); $g2->add_literal_triple('http://talisaspire.com/works/4d101f63c10a6', $g2->qname_to_uri('bibo:isbn13'), '9780393929691-3'); + $this->tripod->saveChanges($g1, $g2, 'http://talisaspire.com/'); $t2 = $this->tripodTables->getTableRows('t_resource', ['_id.r' => 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA-2']); @@ -97,14 +100,14 @@ public function testTripodSaveChangesUpdatesLiteralTripleInTable() $this->assertEquals($expectedIsbn13s, $t2['results'][0]['isbn13']); } - public function testGenerateTableRowsWithCounts() + public function testGenerateTableRowsWithCounts(): void { $this->tripodTables->generateTableRows('t_source_count'); $t1 = $this->tripodTables->getTableRows('t_source_count'); // expecting two rows - $this->assertEquals(count($t1['results']), 3); + $this->assertCount(3, $t1['results']); $result = $t1['results'][0]; // check out the columns @@ -115,14 +118,14 @@ public function testGenerateTableRowsWithCounts() $this->assertArrayHasKey('isbn13', $result, 'Result does not contain isbn13'); } - public function testGenerateTableRowsWithCountUpdateAndRequery() + public function testGenerateTableRowsWithCountUpdateAndRequery(): void { $this->tripodTables->generateTableRows('t_source_count'); $t1 = $this->tripodTables->getTableRows('t_source_count'); // expecting two rows - $this->assertEquals(count($t1['results']), 3); + $this->assertCount(3, $t1['results']); $result = $t1['results'][0]; // check out the columns @@ -143,14 +146,14 @@ public function testGenerateTableRowsWithCountUpdateAndRequery() $t2 = $this->tripodTables->getTableRows('t_source_count'); $result = null; - $this->assertEquals(count($t2['results']), 3); + $this->assertCount(3, $t2['results']); foreach ($t2['results'] as $r) { if ($r['_id']['r'] == $subject) { $result = $r; } } - $this->assertNotNull($result, "Cound not find table row for {$subject}"); + $this->assertNotNull($result, 'Cound not find table row for ' . $subject); // check out the columns $this->assertArrayHasKey('type', $result, 'Result does not contain type'); $this->assertArrayHasKey('source_count', $result, 'Result does not contain source_count'); @@ -158,14 +161,14 @@ public function testGenerateTableRowsWithCountUpdateAndRequery() $this->assertArrayHasKey('isbn13', $result, 'Result does not contain isbn13'); } - public function testGenerateTableRowsWithCountAndRegexUpdateAndRequery() + public function testGenerateTableRowsWithCountAndRegexUpdateAndRequery(): void { $this->tripodTables->generateTableRows('t_source_count_regex'); $t1 = $this->tripodTables->getTableRows('t_source_count_regex'); // expecting two rows - $this->assertEquals(count($t1['results']), 3); + $this->assertCount(3, $t1['results']); $result = $t1['results'][0]; // check out the columns @@ -188,14 +191,14 @@ public function testGenerateTableRowsWithCountAndRegexUpdateAndRequery() $t2 = $this->tripodTables->getTableRows('t_source_count_regex'); $result = null; - $this->assertEquals(count($t2['results']), 3); + $this->assertCount(3, $t2['results']); foreach ($t2['results'] as $r) { if ($r['_id']['r'] == $subject) { $result = $r; } } - $this->assertNotNull($result, "Could not find table row for {$subject}"); + $this->assertNotNull($result, 'Could not find table row for ' . $subject); // check out the columns $this->assertArrayHasKey('type', $result, 'Result does not contain type'); $this->assertArrayHasKey('source_count', $result, 'Result does not contain source_count'); @@ -204,14 +207,14 @@ public function testGenerateTableRowsWithCountAndRegexUpdateAndRequery() $this->assertArrayHasKey('isbn13', $result, 'Result does not contain isbn13'); } - public function testGenerateTableRowsWithCountOnJoinAndRegexUpdateAndRequery() + public function testGenerateTableRowsWithCountOnJoinAndRegexUpdateAndRequery(): void { $this->tripodTables->generateTableRows('t_join_source_count_regex'); $t1 = $this->tripodTables->getTableRows('t_join_source_count_regex', ['_id.r' => 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA-2']); // expecting two rows - $this->assertEquals(count($t1['results']), 1); + $this->assertCount(1, $t1['results']); $result = $t1['results'][0]; // check out the columns @@ -228,7 +231,7 @@ public function testGenerateTableRowsWithCountOnJoinAndRegexUpdateAndRequery() $t2 = $this->tripodTables->getTableRows('t_join_source_count_regex', ['_id.r' => 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA-2']); - $this->assertEquals(count($t2['results']), 1); + $this->assertCount(1, $t2['results']); $result = $t2['results'][0]; // check out the columns @@ -236,7 +239,7 @@ public function testGenerateTableRowsWithCountOnJoinAndRegexUpdateAndRequery() $this->assertEquals(4, $result['titles_count']); } - public function testUpdateWillDeleteItem() + public function testUpdateWillDeleteItem(): void { $mockTables = $this->getMockBuilder(Tripod\Mongo\Composites\Tables::class) ->onlyMethods(['deleteTableRowsForResource', 'generateTableRowsForType']) @@ -248,7 +251,7 @@ public function testUpdateWillDeleteItem() $mockTables->update(new ImpactedSubject(['r' => 'http://foo', 'c' => 'context'], OP_TABLES, 'foo', 'bar', ['t_table'])); } - public function testUpdateWillGenerateRows() + public function testUpdateWillGenerateRows(): void { $mockTables = $this->getMockBuilder(Tripod\Mongo\Composites\Tables::class) ->onlyMethods(['deleteTableRowsForResource', 'generateTableRowsForResource']) @@ -260,23 +263,23 @@ public function testUpdateWillGenerateRows() $mockTables->update(new ImpactedSubject(['r' => 'http://foo', 'c' => 'context'], OP_TABLES, 'foo', 'bar', ['t_table'])); } - public function testGenerateTableRows() + public function testGenerateTableRows(): void { $this->tripodTables->generateTableRows('t_resource'); $t1 = $this->tripodTables->getTableRows('t_resource'); // expecting two rows - $this->assertEquals(count($t1['results']), 3); + $this->assertCount(3, $t1['results']); $result = $t1['results'][0]; // check out the columns - $this->assertTrue(isset($result['type']), 'Result does not contain type'); - $this->assertTrue(isset($result['isbn']), 'Result does not contain isbn'); - $this->assertTrue(isset($result['isbn13']), 'Result does not contain isbn13'); + $this->assertArrayHasKey('type', $result, 'Result does not contain type'); + $this->assertArrayHasKey('isbn', $result, 'Result does not contain isbn'); + $this->assertArrayHasKey('isbn13', $result, 'Result does not contain isbn13'); } - public function testBatchTableRowGeneration() + public function testBatchTableRowGeneration(): void { $count = 234; $docs = []; @@ -341,7 +344,7 @@ public function testBatchTableRowGeneration() $tables->generateTableRows('t_resource', null, null, 'TESTQUEUE'); } - public function testGetTableRowsSort() + public function testGetTableRowsSort(): void { $this->tripodTables->generateTableRows('t_resource'); @@ -355,95 +358,98 @@ public function testGetTableRowsSort() $this->assertEquals('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', $t1['results'][0]['_id']['r']); } - public function testGetTableRowsFilter() + public function testGetTableRowsFilter(): void { $this->tripodTables->generateTableRows('t_resource'); $t1 = $this->tripodTables->getTableRows('t_resource', ['value.isbn' => '9780393929690']); // only bring back rows with isbn = 9780393929690 // expecting one row - $this->assertTrue(count($t1['results']) == 1); + $this->assertCount(1, $t1['results']); $this->assertEquals('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', $t1['results'][0]['_id']['r']); } - public function testGetTableRowsLimitOffset() + public function testGetTableRowsLimitOffset(): void { $this->tripodTables->generateTableRows('t_resource'); $t1 = $this->tripodTables->getTableRows('t_resource', [], ['value.isbn' => 1], 0, 1); // expecting http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA - $this->assertTrue(count($t1['results']) == 1); + $this->assertCount(1, $t1['results']); $this->assertEquals('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', $t1['results'][0]['_id']['r']); $t2 = $this->tripodTables->getTableRows('t_resource', [], ['value.isbn' => 1], 1, 1); // expecting http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA-2 - $this->assertTrue(count($t2['results']) == 1); + $this->assertCount(1, $t2['results']); $this->assertEquals('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA-2', $t2['results'][0]['_id']['r']); } - public function testGenerateTableRowsForResourceUnnamespaced() + public function testGenerateTableRowsForResourceUnnamespaced(): void { $this->tripodTables->update(new ImpactedSubject(['r' => 'http://basedata.com/b/2', 'c' => 'http://basedata.com/b/DefaultGraph'], OP_TABLES, $this->tripodTables->getStoreName(), $this->tripodTables->getPodName(), ['t_work2'])); $rows = $this->tripodTables->getTableRows('t_work2'); - $this->assertTrue($rows['head']['count'] == 1, 'Expected one row'); + $this->assertEquals(1, $rows['head']['count'], 'Expected one row'); } - public function testGenerateTableRowsForResourceNamespaced() + public function testGenerateTableRowsForResourceNamespaced(): void { $this->tripodTables->update(new ImpactedSubject(['r' => 'baseData:2', 'c' => 'baseData:DefaultGraph'], OP_TABLES, $this->tripodTables->getStoreName(), $this->tripodTables->getPodName(), ['t_work2'])); $rows = $this->tripodTables->getTableRows('t_work2'); - $this->assertTrue($rows['head']['count'] == 1, 'Expected one row'); + $this->assertEquals(1, $rows['head']['count'], 'Expected one row'); } - public function testGenerateTableRowsForResourceContextNamespaced() + public function testGenerateTableRowsForResourceContextNamespaced(): void { $this->tripodTables->update(new ImpactedSubject(['r' => 'http://basedata.com/b/2', 'c' => 'baseData:DefaultGraph'], OP_TABLES, $this->tripodTables->getStoreName(), $this->tripodTables->getPodName(), ['t_work2'])); $rows = $this->tripodTables->getTableRows('t_work2'); - $this->assertTrue($rows['head']['count'] == 1, 'Expected one row'); + $this->assertEquals(1, $rows['head']['count'], 'Expected one row'); } - public function testGenerateTableRowsForResourceResourceNamespaced() + public function testGenerateTableRowsForResourceResourceNamespaced(): void { $this->tripodTables->update(new ImpactedSubject(['r' => 'baseData:2', 'c' => 'http://basedata.com/b/DefaultGraph'], OP_TABLES, $this->tripodTables->getStoreName(), $this->tripodTables->getPodName(), ['t_work2'])); $rows = $this->tripodTables->getTableRows('t_work2'); - $this->assertTrue($rows['head']['count'] == 1, 'Expected one row'); + $this->assertEquals(1, $rows['head']['count'], 'Expected one row'); } - public function testGenerateTableRowsForResourcesOfTypeWithNamespace() + public function testGenerateTableRowsForResourcesOfTypeWithNamespace(): void { $mockTripodTables = $this->getMockBuilder(Tripod\Mongo\Composites\Tables::class) ->onlyMethods(['generateTableRows']) ->setConstructorArgs([$this->tripod->getStoreName(), $this->getTripodCollection($this->tripod), 'http://talisaspire.com/']) ->getMock(); - $mockTripodTables->expects($this->atLeastOnce())->method('generateTableRows')->will($this->returnValue(['ok' => true])); + $mockTripodTables->expects($this->atLeastOnce()) + ->method('generateTableRows') + ->with('t_work2', 'http://example.com/1', 'http://talisaspire.com/', null) + ->willReturn(['ok' => true]); // check where referred to as acorn:Work2 in spec... - $mockTripodTables->generateTableRowsForType('http://talisaspire.com/schema#Work2'); + $mockTripodTables->generateTableRowsForType('http://talisaspire.com/schema#Work2', 'http://example.com/1', 'http://talisaspire.com/'); $mockTripodTables = $this->getMockBuilder(Tripod\Mongo\Composites\Tables::class) ->onlyMethods(['generateTableRows']) ->setConstructorArgs([$this->tripod->getStoreName(), $this->getTripodCollection($this->tripod), 'http://talisaspire.com/']) ->getMock(); - $mockTripodTables->expects($this->atLeastOnce())->method('generateTableRows')->will($this->returnValue(['ok' => true])); + $mockTripodTables->expects($this->atLeastOnce())->method('generateTableRows')->willReturn(['ok' => true]); // check where referred to as http://talisaspire.com/schema#Resource in spec... - $mockTripodTables->generateTableRowsForType('acorn:Resource'); + $mockTripodTables->generateTableRowsForType('acorn:Resource', 'http://example.com/2', 'http://talisaspire.com/'); } /** * Test table specification predicate modifier config. */ - public function testGenerateTableRowsForUsersWithModifiersValidConfig() + public function testGenerateTableRowsForUsersWithModifiersValidConfig(): void { $this->expectNotToPerformAssertions(); @@ -506,7 +512,7 @@ public function testGenerateTableRowsForUsersWithModifiersValidConfig() /** * Test invalid table specification predicate modifier config - use a bad attribute. */ - public function testGenerateTableRowsForUsersWithModifiersInvalidConfigBadGlue() + public function testGenerateTableRowsForUsersWithModifiersInvalidConfigBadGlue(): void { $this->expectException(ConfigException::class); $this->expectExceptionMessage("Invalid modifier: 'glue2' in key 'join'"); @@ -536,13 +542,13 @@ public function testGenerateTableRowsForUsersWithModifiersInvalidConfigBadGlue() /** * Test table rows have been generated successfully for a "join" modifier. */ - public function testGenerateTableRowsForUsersWithModifiersJoin() + public function testGenerateTableRowsForUsersWithModifiersJoin(): void { // Get table rows $rows = $this->generateTableRows('t_users'); // We should have 1 result and it should have modified fields - $this->assertTrue($rows['head']['count'] == 1, 'Expected one row'); + $this->assertEquals(1, $rows['head']['count'], 'Expected one row'); $this->assertEquals('Harry Potter', $rows['results'][0]['join']); } @@ -550,13 +556,13 @@ public function testGenerateTableRowsForUsersWithModifiersJoin() /** * Test table rows have been generated for a "join" modifier but with a single value rather than an array. */ - public function testGenerateTableRowsForUsersWithModifiersJoinSingle() + public function testGenerateTableRowsForUsersWithModifiersJoinSingle(): void { // Get table rows $rows = $this->generateTableRows('t_users'); // We should have 1 result and it should have modified fields - $this->assertTrue($rows['head']['count'] == 1, 'Expected one row'); + $this->assertEquals(1, $rows['head']['count'], 'Expected one row'); $this->assertEquals('Harry', $rows['results'][0]['joinSingle']); } @@ -564,13 +570,13 @@ public function testGenerateTableRowsForUsersWithModifiersJoinSingle() /** * Test table rows have been generated for a "lowercase" modifier with a "join" inside it. */ - public function testGenerateTableRowsForUsersWithModifiersJoinLowerCase() + public function testGenerateTableRowsForUsersWithModifiersJoinLowerCase(): void { // Get table rows $rows = $this->generateTableRows('t_users'); // We should have 1 result and it should have modified fields - $this->assertTrue($rows['head']['count'] == 1, 'Expected one row'); + $this->assertEquals(1, $rows['head']['count'], 'Expected one row'); $this->assertEquals('harry potter', $rows['results'][0]['joinLowerCase']); } @@ -578,13 +584,13 @@ public function testGenerateTableRowsForUsersWithModifiersJoinLowerCase() /** * Test table rows have been generated for a "date" modifier. */ - public function testGenerateTableRowsForUsersWithModifiersMongoDate() + public function testGenerateTableRowsForUsersWithModifiersMongoDate(): void { // Get table rows $rows = $this->generateTableRows('t_users'); // We should have 1 result and it should have modified fields - $this->assertTrue($rows['head']['count'] == 1, 'Expected one row'); + $this->assertEquals(1, $rows['head']['count'], 'Expected one row'); $this->assertInstanceOf(UTCDateTime::class, $rows['results'][0]['mongoDate']); } @@ -592,13 +598,13 @@ public function testGenerateTableRowsForUsersWithModifiersMongoDate() /** * Test table rows have been generated for a "date" modifier but with a value that does not exist. */ - public function testGenerateTableRowsForUsersWithModifiersMongoDateDoesNotExist() + public function testGenerateTableRowsForUsersWithModifiersMongoDateDoesNotExist(): void { // Get table rows $rows = $this->generateTableRows('t_users'); // We should have 1 result and it should have modified fields - $this->assertTrue($rows['head']['count'] == 1, 'Expected one row'); + $this->assertEquals(1, $rows['head']['count'], 'Expected one row'); // Test for data that doesn't exist $this->assertArrayNotHasKey('mongoDateDoesNotExist', $rows['results'][0]); @@ -608,13 +614,13 @@ public function testGenerateTableRowsForUsersWithModifiersMongoDateDoesNotExist( * Test table rows have been generated for a "lowercase" modifier wtih a "join" modifier inside. It also has an * extra field attached to the row as well. */ - public function testGenerateTableRowsForUsersWithModifiersJoinLowerCaseAndExtraField() + public function testGenerateTableRowsForUsersWithModifiersJoinLowerCaseAndExtraField(): void { // Get table rows $rows = $this->generateTableRows('t_users'); // We should have 1 result and it should have modified fields - $this->assertTrue($rows['head']['count'] == 1, 'Expected one row'); + $this->assertEquals(1, $rows['head']['count'], 'Expected one row'); $this->assertArrayHasKey('joinLowerCaseANDExtraField', $rows['results'][0]); $this->assertIsArray($rows['results'][0]['joinLowerCaseANDExtraField']); @@ -625,13 +631,13 @@ public function testGenerateTableRowsForUsersWithModifiersJoinLowerCaseAndExtraF /** * Test table rows have been generated for a "date" modifier but with an invalid date string. */ - public function testGenerateTableRowsForUsersWithModifiersDateInvalid() + public function testGenerateTableRowsForUsersWithModifiersDateInvalid(): void { // Get table rows $rows = $this->generateTableRows('t_users'); // We should have 1 result and it should have modified fields - $this->assertTrue($rows['head']['count'] == 1, 'Expected one row'); + $this->assertEquals(1, $rows['head']['count'], 'Expected one row'); // Check borked data // Trying to use date but passed in a string - should default to 0 for sec and usec @@ -642,13 +648,13 @@ public function testGenerateTableRowsForUsersWithModifiersDateInvalid() /** * Test table rows have been generated for a "lowercase" modifier around a "date" modifier. */ - public function testGenerateTableRowsForUsersWithModifiersLowercaseDate() + public function testGenerateTableRowsForUsersWithModifiersLowercaseDate(): void { // Get table rows $rows = $this->generateTableRows('t_users'); // We should have 1 result and it should have modified fields - $this->assertTrue($rows['head']['count'] == 1, 'Expected one row'); + $this->assertEquals(1, $rows['head']['count'], 'Expected one row'); // Lowercasing a mongodate object should be the same as running a __toString() on the date object $this->assertEquals($rows['results'][0]['mongoDate']->__toString(), $rows['results'][0]['lowercaseDate']); @@ -657,7 +663,7 @@ public function testGenerateTableRowsForUsersWithModifiersLowercaseDate() /** * Test table rows are tuncated if they are too large to index. */ - public function testGenerateTableRowsTruncatesFieldsTooLargeToIndex() + public function testGenerateTableRowsTruncatesFieldsTooLargeToIndex(): void { $fullTitle = 'Mahommah Gardo Baquaqua. Biography of Mahommah G. Baquaqua, a Native of Zoogoo, in the Interior of Africa. (A Convert to Christianity,) With a Description of That Part of the World; Including the Manners and Customs of the Inhabitants, Their Religious Notions, Form of Government, Laws, Appearance of the Country, Buildings, Agriculture, Manufactures, Shepherds and Herdsmen, Domestic Animals, Marriage Ceremonials, Funeral Services, Styles of Dress, Trade and Commerce, Modes of Warfare, System of Slavery, &c., &c. Mahommah's Early Life, His Education, His Capture and Slavery in Western Africa and Brazil, His Escape to the United States, from Thence to Hayti, (the City of Port Au Prince,) His Reception by the Baptist Missionary There, The Rev. W. L. Judd; His Conversion to Christianity, Baptism, and Return to This Country, His Views, Objects and Aim. Written and Revised from His Own Words, by Samuel Moore, Esq., Late Publisher of the "North of England Shipping Gazette," Author of Several Popular Works, and Editor of Sundry Reform Papers.'; $truncatedTitle = substr($fullTitle, 0, 1007); // 1007 = 1024 - index name "value_title_1" + Randomness @@ -674,13 +680,13 @@ public function testGenerateTableRowsTruncatesFieldsTooLargeToIndex() // Assert that the title starts with the truncated title. // This will be the case for both Mongo 2.4 and Mongo 2.6 - $this->assertTrue(strpos($rows['results'][0]['title'], $truncatedTitle) === 0, 'Unexpected title'); + $this->assertSame(0, strpos($rows['results'][0]['title'], $truncatedTitle), 'Unexpected title'); } /** * Test that link modifier is derived from the joined resource id, rather than base. */ - public function testJoinLinkValueIsForJoinedResource() + public function testJoinLinkValueIsForJoinedResource(): void { $this->tripodTables->generateTableRows('t_join_link'); $rows = $this->tripodTables->getTableRows('t_join_link', ['_id.r' => 'baseData:foo1234']); @@ -709,7 +715,7 @@ public function testJoinLinkValueIsForJoinedResource() * Test to ensure that impact index contains joined ids for resources that do not yet exist in the database (i.e. * allow open world model). */ - public function testPreviouslyUnavailableDataBecomesPresentAndTriggersTableRegen() + public function testPreviouslyUnavailableDataBecomesPresentAndTriggersTableRegen(): void { $this->tripodTables->generateTableRows('t_join_link'); $rows = $this->tripodTables->getTableRows('t_join_link', ['_id.r' => 'baseData:bar1234']); @@ -725,6 +731,7 @@ public function testPreviouslyUnavailableDataBecomesPresentAndTriggersTableRegen $g = new MongoGraph(); $g->add_resource_triple($uri, $g->qname_to_uri('rdf:type'), $g->qname_to_uri('foaf:Person')); $g->add_literal_triple($uri, $g->qname_to_uri('foaf:name'), 'A. Nonymous'); + $this->tripod->saveChanges(new MongoGraph(), $g, 'http://talisaspire.com/', "This resource didn't exist at join time"); $userGraph = $this->tripod->describeResource($uri); @@ -741,14 +748,14 @@ public function testPreviouslyUnavailableDataBecomesPresentAndTriggersTableRegen /** * Ensure that an array of links is returned if there are multiple resources matched by the join. */ - public function testLinkWorksOnRepeatingPredicatesForResource() + public function testLinkWorksOnRepeatingPredicatesForResource(): void { $this->tripodTables->generateTableRows('t_link_multiple'); $rows = $this->tripodTables->getTableRows('t_link_multiple', ['_id.r' => 'baseData:bar1234']); $this->assertEquals(1, $rows['head']['count']); $this->assertArrayHasKey('contributorLink', $rows['results'][0]); $this->assertTrue(is_array($rows['results'][0]['contributorLink'])); - $this->assertEquals(2, count($rows['results'][0]['contributorLink'])); + $this->assertCount(2, $rows['results'][0]['contributorLink']); $this->assertEquals('http://schemas.talis.com/2005/user/schema#10101', $rows['results'][0]['contributorLink'][0]); $this->assertEquals('http://schemas.talis.com/2005/user/schema#10102', $rows['results'][0]['contributorLink'][1]); } @@ -756,7 +763,7 @@ public function testLinkWorksOnRepeatingPredicatesForResource() /** * Return the distinct values of a table column. */ - public function testDistinct() + public function testDistinct(): void { // Get table rows $table = 't_distinct'; @@ -769,7 +776,7 @@ public function testDistinct() $this->assertArrayHasKey('count', $results['head']); $this->assertEquals(4, $results['head']['count']); $this->assertArrayHasKey('results', $results); - $this->assertEquals(4, count($results['results'])); + $this->assertCount(4, $results['results']); $this->assertContains('Physics 3rd Edition: Physics for Engineers and Scientists', $results['results']); $this->assertContains('A document title', $results['results']); $this->assertContains('Another document title', $results['results']); @@ -780,7 +787,7 @@ public function testDistinct() $this->assertArrayHasKey('count', $results['head']); $this->assertEquals(2, $results['head']['count']); $this->assertArrayHasKey('results', $results); - $this->assertEquals(2, count($results['results'])); + $this->assertCount(2, $results['results']); $this->assertNotContains('Physics 3rd Edition: Physics for Engineers and Scientists', $results['results']); $this->assertContains('A document title', $results['results']); $this->assertContains('Another document title', $results['results']); @@ -790,7 +797,7 @@ public function testDistinct() $this->assertArrayHasKey('count', $results['head']); $this->assertEquals(7, $results['head']['count']); $this->assertArrayHasKey('results', $results); - $this->assertEquals(7, count($results['results'])); + $this->assertCount(7, $results['results']); $this->assertContains('acorn:Resource', $results['results']); $this->assertContains('acorn:Work', $results['results']); $this->assertContains('bibo:Book', $results['results']); @@ -800,18 +807,18 @@ public function testDistinct() /** * Return no results for tablespec that doesn't exist. */ - public function testDistinctOnTableSpecThatDoesNotExist() + public function testDistinctOnTableSpecThatDoesNotExist(): void { $table = 't_nothing_to_see_here'; $this->expectException(ConfigException::class); - $this->expectExceptionMessage('Table id \'t_nothing_to_see_here\' not in configuration'); - $results = $this->tripodTables->distinct($table, 'value.foo'); + $this->expectExceptionMessage("Table id 't_nothing_to_see_here' not in configuration"); + $this->tripodTables->distinct($table, 'value.foo'); } /** * Return no results for distinct on a fieldname that is not defined in tableSpec. */ - public function testDistinctOnFieldNameThatIsNotInTableSpec() + public function testDistinctOnFieldNameThatIsNotInTableSpec(): void { // Get table rows $table = 't_distinct'; @@ -825,7 +832,7 @@ public function testDistinctOnFieldNameThatIsNotInTableSpec() /** * Return no results for filters that match no table rows. */ - public function testDistinctForFilterWithNoMatches() + public function testDistinctForFilterWithNoMatches(): void { // Get table rows $table = 't_distinct'; @@ -836,9 +843,9 @@ public function testDistinctForFilterWithNoMatches() $this->assertEmpty($results['results']); } - public function testTableRowsGenerateWhenDefinedPredicateChanges() + public function testTableRowsGenerateWhenDefinedPredicateChanges(): void { - foreach (Config::getInstance()->getTableSpecifications($this->tripod->getStoreName()) as $specId => $spec) { + foreach (array_keys(Config::getInstance()->getTableSpecifications($this->tripod->getStoreName())) as $specId) { $this->generateTableRows($specId); } @@ -892,7 +899,7 @@ public function testTableRowsGenerateWhenDefinedPredicateChanges() $tripod->expects($this->once()) ->method('getComposite') ->with(OP_TABLES) - ->will($this->returnValue($tables)); + ->willReturn($tables); // Walk through the processSyncOperations process manually for tables @@ -944,9 +951,9 @@ public function testTableRowsGenerateWhenDefinedPredicateChanges() } } - public function testTableRowsNotGeneratedWhenUndefinedPredicateChanges() + public function testTableRowsNotGeneratedWhenUndefinedPredicateChanges(): void { - foreach (Config::getInstance()->getTableSpecifications($this->tripod->getStoreName()) as $specId => $spec) { + foreach (array_keys(Config::getInstance()->getTableSpecifications($this->tripod->getStoreName())) as $specId) { $this->generateTableRows($specId); } @@ -970,7 +977,7 @@ public function testTableRowsNotGeneratedWhenUndefinedPredicateChanges() $this->assertEmpty($impactedSubjects); } - public function testUpdateOfResourceInImpactIndexTriggersRegenerationTableRows() + public function testUpdateOfResourceInImpactIndexTriggersRegenerationTableRows(): void { $mockTables = $this->getMockBuilder(Tripod\Mongo\Composites\Tables::class) ->onlyMethods(['generateTableRows']) @@ -1035,7 +1042,7 @@ public function testUpdateOfResourceInImpactIndexTriggersRegenerationTableRows() } } - public function testRdfTypeTriggersGenerationOfTableRows() + public function testRdfTypeTriggersGenerationOfTableRows(): void { $uri = 'http://example.com/resources/' . uniqid(); @@ -1047,7 +1054,8 @@ public function testRdfTypeTriggersGenerationOfTableRows() $subjectsAndPredicatesOfChange = [ $labeller->uri_to_alias($uri) => [ - 'rdf:type', 'bibo:issn', + 'rdf:type', + 'bibo:issn', ], ]; @@ -1088,7 +1096,7 @@ public function testRdfTypeTriggersGenerationOfTableRows() $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $mockTripodUpdates->expects($this->once()) ->method('processSyncOperations') @@ -1145,7 +1153,7 @@ public function testRdfTypeTriggersGenerationOfTableRows() } } - public function testUpdateToResourceWithMatchingRdfTypeShouldOnlyRegenerateIfRdfTypeIsPartOfUpdate() + public function testUpdateToResourceWithMatchingRdfTypeShouldOnlyRegenerateIfRdfTypeIsPartOfUpdate(): void { $uri = 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA'; $labeller = new Labeller(); @@ -1180,7 +1188,7 @@ public function testUpdateToResourceWithMatchingRdfTypeShouldOnlyRegenerateIfRdf $this->assertEquals($expectedImpactedSubjects, $impactedSubjects); } - public function testNewResourceThatDoesNotMatchAnythingCreatesNoImpactedSubjects() + public function testNewResourceThatDoesNotMatchAnythingCreatesNoImpactedSubjects(): void { $uri = 'http://example.com/resources/' . uniqid(); $labeller = new Labeller(); @@ -1229,7 +1237,7 @@ public function testNewResourceThatDoesNotMatchAnythingCreatesNoImpactedSubjects $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $mockTripodUpdates->expects($this->once()) ->method('processSyncOperations') @@ -1252,7 +1260,7 @@ public function testNewResourceThatDoesNotMatchAnythingCreatesNoImpactedSubjects $this->assertEmpty($tables->getImpactedSubjects($subjectsAndPredicatesOfChange, $this->defaultContext)); } - public function testDeleteResourceCreatesImpactedSubjects() + public function testDeleteResourceCreatesImpactedSubjects(): void { $uri = 'http://example.com/users/' . uniqid(); $labeller = new Labeller(); @@ -1292,7 +1300,7 @@ public function testDeleteResourceCreatesImpactedSubjects() $graph2->add_literal_triple( $uri2, $labeller->qname_to_uri('foaf:surname'), - 'O\'ther' + "O'ther" ); // Save the graphs and ensure that table rows are generated @@ -1368,7 +1376,7 @@ public function testDeleteResourceCreatesImpactedSubjects() $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $expectedSubjectsAndPredicatesOfChange = [ $uriAlias => ['rdf:type', 'foaf:firstName', 'foaf:surname'], @@ -1449,7 +1457,7 @@ public function testDeleteResourceCreatesImpactedSubjects() * Save several new resources in a single operation. Only one of the resources has a type that is applicable based on specifications, * therefore only one ImpactedSubject should be created. */ - public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject() + public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject(): void { $tripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getDataUpdater']) @@ -1486,7 +1494,7 @@ public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject() $tripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdates)); + ->willReturn($tripodUpdates); // first lets add a book, which should trigger a search doc, view and table gen for a single item $g = new MongoGraph(); @@ -1512,6 +1520,7 @@ public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject() $g->add_literal_triple($newSubjectUri3, $g->qname_to_uri('dct:title'), 'This is yet another new resource'); $g->add_literal_triple($newSubjectUri3, $g->qname_to_uri('dct:subject'), 'art'); $g->add_literal_triple($newSubjectUri3, $g->qname_to_uri('dct:subject'), 'design'); + $subjectsAndPredicatesOfChange = [ $newSubjectUri1 => ['rdf:type', 'dct:creator', 'dct:title', 'dct:subject'], $newSubjectUri2 => ['rdf:type', 'dct:creator', 'dct:title', 'dct:subject'], @@ -1539,9 +1548,9 @@ public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject() $this->assertEquals($expectedImpactedSubjects, $impactedSubjects); } - public function testRemoveTableSpecDoesNotAffectInvalidation() + public function testRemoveTableSpecDoesNotAffectInvalidation(): void { - foreach (Config::getInstance()->getTableSpecifications($this->tripod->getStoreName()) as $specId => $spec) { + foreach (array_keys(Config::getInstance()->getTableSpecifications($this->tripod->getStoreName())) as $specId) { $this->generateTableRows($specId); } @@ -1588,7 +1597,7 @@ public function testRemoveTableSpecDoesNotAffectInvalidation() $mockTripod->expects($this->once()) ->method('getComposite') ->with(OP_TABLES) - ->will($this->returnValue($mockTables)); + ->willReturn($mockTables); $mockTables->expects($this->never()) ->method('update'); @@ -1603,7 +1612,7 @@ public function testRemoveTableSpecDoesNotAffectInvalidation() $this->assertGreaterThan(0, $collection->count(['_id.type' => 't_resource', 'value._impactIndex' => [_ID_RESOURCE => $uri, _ID_CONTEXT => $context]])); } - public function testCountTables() + public function testCountTables(): void { $collection = $this->getMockBuilder(Collection::class) ->setConstructorArgs([new Manager(), 'db', 'coll']) @@ -1617,19 +1626,19 @@ public function testCountTables() $tables->expects($this->once()) ->method('getCollectionForTableSpec') ->with('t_source_count') - ->will($this->returnValue($collection)); + ->willReturn($collection); $collection->expects($this->once()) ->method('count') ->with(['_id.type' => 't_source_count']) - ->will($this->returnValue(50)); + ->willReturn(50); $this->assertEquals(50, $tables->count('t_source_count')); } - public function testCountTablesWithFilters() + public function testCountTablesWithFilters(): void { - $filters = ['_cts' => ['$lte' => new UTCDateTime(null)]]; + $filters = ['_cts' => ['$lte' => new UTCDateTime()]]; $query = array_merge(['_id.type' => 't_source_count'], $filters); $collection = $this->getMockBuilder(Collection::class) ->setConstructorArgs([new Manager(), 'db', 'coll']) @@ -1643,17 +1652,17 @@ public function testCountTablesWithFilters() $tables->expects($this->once()) ->method('getCollectionForTableSpec') ->with('t_source_count') - ->will($this->returnValue($collection)); + ->willReturn($collection); $collection->expects($this->once()) ->method('count') ->with($query) - ->will($this->returnValue(37)); + ->willReturn(37); $this->assertEquals(37, $tables->count('t_source_count', $filters)); } - public function testDeleteTableRowsByTableId() + public function testDeleteTableRowsByTableId(): void { $collection = $this->getMockBuilder(Collection::class) ->setConstructorArgs([new Manager(), 'db', 'coll']) @@ -1667,7 +1676,7 @@ public function testDeleteTableRowsByTableId() $deleteResult->expects($this->once()) ->method('getDeletedCount') - ->will($this->returnValue(2)); + ->willReturn(2); $tables = $this->getMockBuilder(Tripod\Mongo\Composites\Tables::class) ->onlyMethods(['getCollectionForTableSpec']) @@ -1677,19 +1686,19 @@ public function testDeleteTableRowsByTableId() $tables->expects($this->once()) ->method('getCollectionForTableSpec') ->with('t_source_count') - ->will($this->returnValue($collection)); + ->willReturn($collection); $collection->expects($this->once()) ->method('deleteMany') ->with(['_id.type' => 't_source_count']) - ->will($this->returnValue($deleteResult)); + ->willReturn($deleteResult); $this->assertEquals(2, $tables->deleteTableRowsByTableId('t_source_count')); } - public function testDeleteTableRowsByTableIdWithTimestamp() + public function testDeleteTableRowsByTableIdWithTimestamp(): void { - $timestamp = new UTCDateTime(null); + $timestamp = new UTCDateTime(); $query = [ '_id.type' => 't_source_count', @@ -1710,7 +1719,7 @@ public function testDeleteTableRowsByTableIdWithTimestamp() $deleteResult->expects($this->once()) ->method('getDeletedCount') - ->will($this->returnValue(11)); + ->willReturn(11); $tables = $this->getMockBuilder(Tripod\Mongo\Composites\Tables::class) ->onlyMethods(['getCollectionForTableSpec']) @@ -1720,17 +1729,17 @@ public function testDeleteTableRowsByTableIdWithTimestamp() $tables->expects($this->once()) ->method('getCollectionForTableSpec') ->with('t_source_count') - ->will($this->returnValue($collection)); + ->willReturn($collection); $collection->expects($this->once()) ->method('deleteMany') ->with($query) - ->will($this->returnValue($deleteResult)); + ->willReturn($deleteResult); $this->assertEquals(11, $tables->deleteTableRowsByTableId('t_source_count', $timestamp)); } - public function testTablesDocuments() + public function testTablesDocuments(): void { $dbDoc = [ '_id' => [ @@ -1751,7 +1760,7 @@ public function testTablesDocuments() 'code' => 'XMEN-004', 'nodeUrl' => 'http://talis.com/modules/xmen-004', 'name' => 'Psychology: Living with The Voices', - 'description' => 'Professor Deadpool will attempt to give you the ability to embrace your mental disorder and use it to your advantage. Whether you suffer from Schizophrenia, Dissociative Identity Disorder or plain old Comic Awareness, Wade Wilson has probably suffered through it himself. And while Deadpool can\'t solve your problem, he can teach you how to make it one of your most marketable qualities.', + 'description' => "Professor Deadpool will attempt to give you the ability to embrace your mental disorder and use it to your advantage. Whether you suffer from Schizophrenia, Dissociative Identity Disorder or plain old Comic Awareness, Wade Wilson has probably suffered through it himself. And while Deadpool can't solve your problem, he can teach you how to make it one of your most marketable qualities.", 'parentCode' => 'XMEN-001', 'parentNodeUrl' => 'http://talis.com/schools/xmen-001', 'listCount' => 0, @@ -1770,7 +1779,7 @@ public function testTablesDocuments() $this->assertArrayNotHasKey('type', $doc['_id']); } - public function testGetTableRowsNoCount() + public function testGetTableRowsNoCount(): void { $this->tripodTables->generateTableRows('t_resource'); @@ -1780,7 +1789,7 @@ public function testGetTableRowsNoCount() $this->assertEquals(-1, $tableRows['head']['count']); } - public function testGetTableRowsReturnCursor() + public function testGetTableRowsReturnCursor(): void { $this->tripodTables->generateTableRows('t_resource'); @@ -1792,20 +1801,19 @@ public function testGetTableRowsReturnCursor() $this->assertInstanceOf(Tables::class, $result); $count++; } - $this->assertEquals(1, $count); + + $this->assertSame(1, $count); $this->assertGreaterThan(1, $tableRows['head']['count']); } /** * Generate dummy config that we can use for creating a Config object. * - * @return array + * @return array */ - private function generateMongoTripodTestConfig() + private function generateMongoTripodTestConfig(): array { - $config = []; - $config['defaultContext'] = 'http://talisaspire.com/'; - $config['data_sources'] = [ + return ['defaultContext' => 'http://talisaspire.com/', 'data_sources' => [ 'db' => [ 'type' => 'mongo', 'connection' => 'mongodb://localhost', @@ -1814,33 +1822,24 @@ private function generateMongoTripodTestConfig() 'type' => 'mongo', 'connection' => 'mongodb://tloghost:27017,tloghost:27018', ], - ]; - $config['stores'] = [ + ], 'stores' => [ $this->defaultStoreName => [ 'data_source' => 'db', 'pods' => [ $this->defaultPodName => [], ], ], - ]; - $config['queue'] = ['database' => 'queue', 'collection' => 'q_queue', 'data_source' => 'db']; - $config['transaction_log'] = [ + ], 'queue' => ['database' => 'queue', 'collection' => 'q_queue', 'data_source' => 'db'], 'transaction_log' => [ 'database' => 'transactions', 'collection' => 'transaction_log', 'data_source' => 'db', - ]; - - return $config; + ]]; } /** * Generate table rows based off an id. - * - * @param string $id - * - * @return array */ - private function generateTableRows($id) + private function generateTableRows(string $id): array { $this->tripodTables->generateTableRows($id); diff --git a/test/unit/mongo/MongoTripodTestBase.php b/test/unit/mongo/MongoTripodTestBase.php index eb7e71f1..f17b8065 100644 --- a/test/unit/mongo/MongoTripodTestBase.php +++ b/test/unit/mongo/MongoTripodTestBase.php @@ -1,8 +1,11 @@ %s\n", get_class($this), $this->getName()); @@ -50,7 +55,7 @@ protected function tearDown(): void $this->tripodTransactionLog = null; } - protected function loadResourceData() + protected function loadResourceData(): void { $docs = json_decode(file_get_contents(__DIR__ . '/data/resources.json'), true); foreach ($docs as $d) { @@ -58,22 +63,22 @@ protected function loadResourceData() } } - protected function loadDatesDataViaTripod() + protected function loadDatesDataViaTripod(): void { $this->loadDataViaTripod($this->tripod, '/data/dates.json'); } - protected function loadResourceDataViaTripod() + protected function loadResourceDataViaTripod(): void { $this->loadDataViaTripod($this->tripod, '/data/resources.json'); } - protected function loadBaseSearchDataViaTripod() + protected function loadBaseSearchDataViaTripod(): void { $this->loadDataViaTripod($this->tripod, '/data/searchData.json'); } - protected function loadRelatedContentIntoTripod() + protected function loadRelatedContentIntoTripod(): void { $relatedContentTripod = new Driver( 'CBD_test_related_content', @@ -87,14 +92,14 @@ protected function loadRelatedContentIntoTripod() $this->loadDataViaTripod($relatedContentTripod, '/data/relatedContent.json'); } - protected function getConfigLocation() + protected function getConfigLocation(): string { return __DIR__ . '/data/config.json'; } // HELPERS BELOW HERE - protected function addDocument($doc, $toTransactionLog = false) + protected function addDocument($doc, $toTransactionLog = false): InsertOneResult { $config = Config::getInstance(); if ($toTransactionLog == true) { @@ -107,10 +112,7 @@ protected function addDocument($doc, $toTransactionLog = false) )->insertOne($doc, ['w' => 1]); } - /** - * @return Collection - */ - protected function getTlogCollection() + protected function getTlogCollection(): Collection { $config = Config::getInstance(); $tLogConfig = $config->getTransactionLogConfig(); @@ -118,10 +120,7 @@ protected function getTlogCollection() return $config->getTransactionLogDatabase()->selectCollection($tLogConfig['collection']); } - /** - * @return Collection - */ - protected function getTripodCollection(Driver $tripod) + protected function getTripodCollection(Driver $tripod): Collection { $config = Config::getInstance(); $podName = $tripod->getPodName(); @@ -134,21 +133,19 @@ protected function getTripodCollection(Driver $tripod) } /** - * @param mixed $_id + * @param array|string $_id * @param Collection|IDriver|null $collection - * @param bool $fromTransactionLog - * - * @return array|null */ - protected function getDocument($_id, $collection = null, $fromTransactionLog = false) + protected function getDocument($_id, $collection = null, bool $fromTransactionLog = false): ?array { - if ($fromTransactionLog == true) { + if ($fromTransactionLog) { return $this->tripodTransactionLog->getTransaction($_id); } if ($collection == null) { return $this->getTripodCollection($this->tripod)->findOne(['_id' => $_id]); } + if ($collection instanceof Driver) { return $this->getTripodCollection($collection)->findOne(['_id' => $_id]); } @@ -161,16 +158,20 @@ protected function getDocument($_id, $collection = null, $fromTransactionLog = f * @param int $expectedNumberOfAdditions * @param int $expectedNumberOfRemovals */ - protected function assertChangesForGivenSubject(array $changes, $subjectOfChange, $expectedNumberOfAdditions, $expectedNumberOfRemovals) + protected function assertChangesForGivenSubject(array $changes, $subjectOfChange, $expectedNumberOfAdditions, $expectedNumberOfRemovals): void { $changeSet = null; foreach ($changes as $c) { - if (strpos($c['_id']['r'], '_:cs') !== false) { - if ($c['cs:subjectOfChange']['u'] == $subjectOfChange) { - $changeSet = $c; - } + if (strpos($c['_id']['r'], '_:cs') === false) { + continue; + } + + if ($c['cs:subjectOfChange']['u'] != $subjectOfChange) { + continue; } + + $changeSet = $c; } $this->assertNotNull($changeSet, 'No change set found for the specified subject of change'); @@ -183,6 +184,7 @@ protected function assertChangesForGivenSubject(array $changes, $subjectOfChange $actualAdditions = count($changeSet['cs:addition']); } } + $this->assertEquals($expectedNumberOfAdditions, $actualAdditions, 'Number of additions did not match expectd value'); $actualRemovals = 0; @@ -198,23 +200,16 @@ protected function assertChangesForGivenSubject(array $changes, $subjectOfChange } /** - * @param string $key + * @param array $doc */ - protected function assertTransactionDate(array $doc, $key) + protected function assertTransactionDate(array $doc, string $key): void { - $this->assertTrue(isset($doc[$key]), 'the date property: {$key} was not present in document'); + $this->assertArrayHasKey($key, $doc, 'the date property: {$key} was not present in document'); $this->assertInstanceOf(UTCDateTime::class, $doc[$key]); - $this->assertNotEmpty($doc[$key]->toDateTime()); + $this->assertInstanceOf(DateTimeInterface::class, $doc[$key]->toDateTime()); } - /** - * @param mixed $_id - * @param int|null $expectedValue - * @param bool $hasVersion - * @param Driver|null $tripod - * @param bool $fromTransactionLog - */ - protected function assertDocumentVersion($_id, $expectedValue = null, $hasVersion = true, $tripod = null, $fromTransactionLog = false) + protected function assertDocumentVersion(array $_id, ?int $expectedValue = null, bool $hasVersion = true, ?Driver $tripod = null): void { // just make sure $_id is aliased $labeller = new Labeller(); @@ -222,27 +217,26 @@ protected function assertDocumentVersion($_id, $expectedValue = null, $hasVersio $_id[$key] = $labeller->uri_to_alias($value); } - $doc = $this->getDocument($_id, $tripod, $fromTransactionLog); - if ($hasVersion == true) { - $this->assertTrue(isset($doc['_version']), 'Document for ' . var_export($_id, true) . ' should have a version, but none found'); + $doc = $this->getDocument($_id, $tripod); + if ($hasVersion) { + $this->assertArrayHasKey('_version', $doc, 'Document for ' . var_export($_id, true) . ' should have a version, but none found'); if ($expectedValue !== null) { // echo $expectedValue.":".$doc['_version']; $this->assertEquals($expectedValue, $doc['_version'], 'Document version does not match expected version'); } } else { - $this->assertFalse(isset($doc['_version']), 'Was not expecting document to have a version'); + $this->assertArrayNotHasKey('_version', $doc, 'Was not expecting document to have a version'); } } /** - * @param array $_id the id of the document to retrieve from mongo - * @param string $property the property you are checking for - * @param mixed $expectedValue if not null the property value will be matched against this expectedValue - * @param Collection|IDriver|null $tripod where to retrieve the document from - * @param bool $fromTransactionLog if you want to retrieve the document from transaction log + * @param array $_id the id of the document to retrieve from mongo + * @param string $property the property you are checking for + * @param mixed $expectedValue if not null the property value will be matched against this expectedValue + * @param Collection|IDriver|null $tripod where to retrieve the document from */ - protected function assertDocumentHasProperty(array $_id, $property, $expectedValue = null, $tripod = null, $fromTransactionLog = false) + protected function assertDocumentHasProperty(array $_id, string $property, $expectedValue = null, $tripod = null): void { // just make sure $_id is aliased $labeller = new Labeller(); @@ -250,11 +244,11 @@ protected function assertDocumentHasProperty(array $_id, $property, $expectedVal $_id[$key] = $labeller->uri_to_alias($value); } - $doc = $this->getDocument($_id, $tripod, $fromTransactionLog); + $doc = $this->getDocument($_id, $tripod); - $this->assertTrue(isset($doc[$property]), 'Document for ' . var_export($_id, true) . " should have property [{$property}], but none found"); + $this->assertArrayHasKey($property, $doc, 'Document for ' . var_export($_id, true) . sprintf(' should have property [%s], but none found', $property)); if ($expectedValue !== null) { - $this->assertEquals($expectedValue, $doc[$property], "Document property [{$property}] actual value [" . print_r($doc[$property], true) . '] does not match expected value [' . print_r($expectedValue, true) . ']'); + $this->assertEquals($expectedValue, $doc[$property], sprintf('Document property [%s] actual value [', $property) . print_r($doc[$property], true) . '] does not match expected value [' . print_r($expectedValue, true) . ']'); } } @@ -264,7 +258,7 @@ protected function assertDocumentHasProperty(array $_id, $property, $expectedVal * @param Collection|IDriver|null $tripod where to retrieve the document from * @param bool $fromTransactionLog if you want to retrieve the document from transaction log */ - protected function assertDocumentDoesNotHaveProperty($_id, $property, $tripod = null, $fromTransactionLog = false) + protected function assertDocumentDoesNotHaveProperty(array $_id, string $property, $tripod = null, bool $fromTransactionLog = false): void { // just make sure $_id is aliased $labeller = new Labeller(); @@ -273,16 +267,19 @@ protected function assertDocumentDoesNotHaveProperty($_id, $property, $tripod = } $doc = $this->getDocument($_id, $tripod, $fromTransactionLog); + if ($doc === null) { + $this->assertNull($doc); // @phpstan-ignore method.alreadyNarrowedType + + return; // if document doesn't exist then it doesn't have the property, so assertion is successful + } - $this->assertFalse(isset($doc[$property]), 'Document for ' . var_export($_id, true) . " should not have property [{$property}], but propert was found"); + $this->assertArrayNotHasKey($property, $doc, 'Document for ' . var_export($_id, true) . sprintf(' should not have property [%s], but propert was found', $property)); } /** - * @param mixed $_id - * @param Driver|null $tripod - * @param bool $fromTransactionLog + * @param Collection|IDriver|null $tripod */ - protected function assertDocumentExists($_id, $tripod = null, $fromTransactionLog = false) + protected function assertDocumentExists(array $_id, $tripod = null, bool $fromTransactionLog = false): void { $doc = $this->getDocument($_id, $tripod, $fromTransactionLog); $this->assertNotNull($doc); @@ -290,19 +287,17 @@ protected function assertDocumentExists($_id, $tripod = null, $fromTransactionLo } /** - * @param mixed $_id * @param Driver|null $tripod - * @param bool $useTransactionTripod */ - protected function assertDocumentHasBeenDeleted($_id, $tripod = null, $useTransactionTripod = false) + protected function assertDocumentHasBeenDeleted(array $_id, $tripod = null, bool $useTransactionTripod = false): void { $doc = $this->getDocument($_id, $tripod, $useTransactionTripod); if ($useTransactionTripod) { - $this->assertNull($doc, "Document with _id:[{$_id}] exists, but it should not"); + $this->assertNull($doc, 'Document with _id:[' . print_r($_id, true) . '] exists, but it should not'); } else { $this->assertTrue(is_array($doc), 'Document should be array'); $keys = array_keys($doc); - $this->assertEquals(4, count($keys)); + $this->assertCount(4, $keys); $this->assertArrayHasKey('_id', $doc); $this->assertArrayHasKey(_VERSION, $doc); $this->assertArrayHasKey(_CREATED_TS, $doc); @@ -311,50 +306,41 @@ protected function assertDocumentHasBeenDeleted($_id, $tripod = null, $useTransa } /** - * @param string $s - * @param string $p * @param string $o */ - protected function assertHasLiteralTriple(ExtendedGraph $graph, $s, $p, $o) + protected function assertHasLiteralTriple(ExtendedGraph $graph, string $s, string $p, $o): void { - $this->assertTrue($graph->has_literal_triple($s, $p, $o), "Graph did not contain the literal triple: <{$s}> <{$p}> \"{$o}\""); + $this->assertTrue($graph->has_literal_triple($s, $p, $o), sprintf('Graph did not contain the literal triple: <%s> <%s> "%s"', $s, $p, $o)); } /** - * @param string $s - * @param string $p * @param string $o */ - protected function assertHasResourceTriple(ExtendedGraph $graph, $s, $p, $o) + protected function assertHasResourceTriple(ExtendedGraph $graph, string $s, string $p, $o): void { - $this->assertTrue($graph->has_resource_triple($s, $p, $o), "Graph did not contain the resource triple: <{$s}> <{$p}> <{$o}>"); + $this->assertTrue($graph->has_resource_triple($s, $p, $o), sprintf('Graph did not contain the resource triple: <%s> <%s> <%s>', $s, $p, $o)); } /** - * @param string $s - * @param string $p * @param string $o */ - protected function assertDoesNotHaveLiteralTriple(ExtendedGraph $graph, $s, $p, $o) + protected function assertDoesNotHaveLiteralTriple(ExtendedGraph $graph, string $s, string $p, $o): void { - $this->assertFalse($graph->has_literal_triple($s, $p, $o), "Graph should not contain the literal triple: <{$s}> <{$p}> \"{$o}\""); + $this->assertFalse($graph->has_literal_triple($s, $p, $o), sprintf('Graph should not contain the literal triple: <%s> <%s> "%s"', $s, $p, $o)); } /** - * @param string $s - * @param string $p * @param string $o */ - protected function assertDoesNotHaveResourceTriple(ExtendedGraph $graph, $s, $p, $o) + protected function assertDoesNotHaveResourceTriple(ExtendedGraph $graph, string $s, string $p, $o): void { - $this->assertFalse($graph->has_resource_triple($s, $p, $o), "Graph should not contain the resource triple: <{$s}> <{$p}> <{$o}>"); + $this->assertFalse($graph->has_resource_triple($s, $p, $o), sprintf('Graph should not contain the resource triple: <%s> <%s> <%s>', $s, $p, $o)); } /** - * @param string $subject * @param string $transaction_id */ - protected function lockDocument($subject, $transaction_id) + protected function lockDocument(?string $subject, $transaction_id): void { $collection = Config::getInstance()->getCollectionForLocks('tripod_php_testing'); $labeller = new Labeller(); @@ -384,9 +370,9 @@ protected function getMockStat($host, $port, $prefix = '', array $mockedMethods } /** - * @return array + * @return array|class-string> */ - protected function getStatsDConfig() + protected function getStatsDConfig(): array { return [ 'class' => StatsD::class, @@ -398,10 +384,7 @@ protected function getStatsDConfig() ]; } - /** - * @param string $filename - */ - private function loadDataViaTripod(Driver $tripod, $filename) + private function loadDataViaTripod(Driver $tripod, string $filename): void { $docs = json_decode(file_get_contents(__DIR__ . $filename), true); foreach ($docs as $d) { @@ -430,7 +413,7 @@ class TripodTestConfig extends Tripod\Mongo\Config */ public function __construct() {} - public function loadConfig(array $config) + public function loadConfig(array $config): void { parent::loadConfig($config); } diff --git a/test/unit/mongo/MongoTripodTransactionRollbackTest.php b/test/unit/mongo/MongoTripodTransactionRollbackTest.php index 413d24f8..1e48f6e4 100644 --- a/test/unit/mongo/MongoTripodTransactionRollbackTest.php +++ b/test/unit/mongo/MongoTripodTransactionRollbackTest.php @@ -1,5 +1,7 @@ tripod = $tripod; } - public function testTransactionRollbackDuringLockAllDocuments() + public function testTransactionRollbackDuringLockAllDocuments(): void { // Save some basic data into the db before we create a transaction to modify it $subjectOne = 'http://example.com/resources/1'; @@ -107,14 +109,14 @@ public function testTransactionRollbackDuringLockAllDocuments() $mockTripodUpdate->expects($this->exactly(1)) ->method('generateTransactionId') - ->will($this->returnValue($mockTransactionId)); + ->willReturn($mockTransactionId); $mockTripodUpdate->expects($this->exactly(2 * 20)) // 20 retries for 2 subjects ->method('lockSingleDocument') - ->will($this->returnCallback([$this, 'lockSingleDocumentCauseFailureCallback'])); + ->willReturnCallback([$this, 'lockSingleDocumentCauseFailureCallback']); $mockTripod->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdate)); + ->willReturn($mockTripodUpdate); $mockTripod->setTransactionLog($this->tripodTransactionLog); @@ -142,12 +144,11 @@ public function testTransactionRollbackDuringLockAllDocuments() $this->assertDocumentDoesNotHaveProperty(['r' => $subjectTwo, 'c' => 'http://talisaspire.com/'], _LOCKED_FOR_TRANS_TS, $this->tripod); $transaction = $mockTripodUpdate->getTransactionLog()->getTransaction($mockTransactionId); - $this->assertNotNull($transaction); $this->assertEquals('Did not obtain locks on documents', $transaction['error']['reason']); $this->assertEquals('failed', $transaction['status']); } - public function testTransactionRollbackDuringLockAllDocumentsWithEmptyOriginalCBDS() + public function testTransactionRollbackDuringLockAllDocumentsWithEmptyOriginalCBDS(): void { $subjectOne = 'http://example.com/resources/1'; $subjectTwo = 'http://example.com/resources/2'; @@ -174,14 +175,14 @@ public function testTransactionRollbackDuringLockAllDocumentsWithEmptyOriginalCB $mockTripodUpdate->expects($this->exactly(1)) ->method('generateTransactionId') - ->will($this->returnValue($mockTransactionId)); + ->willReturn($mockTransactionId); $mockTripodUpdate->expects($this->exactly(2 * 20)) // 20 retries for 2 subjects ->method('lockSingleDocument') - ->will($this->returnCallback([$this, 'lockSingleDocumentCauseFailureCallback'])); + ->willReturnCallback([$this, 'lockSingleDocumentCauseFailureCallback']); $mockTripod->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdate)); + ->willReturn($mockTripodUpdate); $mockTripod->setTransactionLog($this->tripodTransactionLog); @@ -202,12 +203,11 @@ public function testTransactionRollbackDuringLockAllDocumentsWithEmptyOriginalCB $this->assertDocumentDoesNotHaveProperty(['r' => $subjectTwo, 'c' => 'http://talisaspire.com/'], _LOCKED_FOR_TRANS_TS, $this->tripod); $transaction = $mockTripodUpdate->getTransactionLog()->getTransaction($mockTransactionId); - $this->assertNotNull($transaction); $this->assertEquals('Did not obtain locks on documents', $transaction['error']['reason']); $this->assertEquals('failed', $transaction['status']); } - public function testTransactionRollbackDuringCreateTransaction() + public function testTransactionRollbackDuringCreateTransaction(): void { // Save some basic data into the db before we create a transaction to modify it $subjectOne = 'http://example.com/resources/1'; @@ -259,13 +259,13 @@ public function testTransactionRollbackDuringCreateTransaction() ->getMock(); $mockTransactionLog->expects($this->once()) ->method('createNewTransaction') - ->will($this->throwException($mockExpectedException)); + ->willThrowException($mockExpectedException); $mockTransactionLog->expects($this->once()) ->method('cancelTransaction') - ->with($this->equalTo($mockTransactionId), $this->equalTo($mockExpectedException)); + ->with($mockTransactionId, $mockExpectedException); $mockTransactionLog->expects($this->once()) ->method('failTransaction') - ->with($this->equalTo($mockTransactionId)); + ->with($mockTransactionId); $mockTripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getDataUpdater']) @@ -278,16 +278,16 @@ public function testTransactionRollbackDuringCreateTransaction() $mockTripodUpdate->expects($this->once()) ->method('generateTransactionId') - ->will($this->returnValue($mockTransactionId)); + ->willReturn($mockTransactionId); $mockTripodUpdate->expects($this->exactly(2)) ->method('lockSingleDocument') - ->will($this->returnCallback([$this, 'lockSingleDocumentCallback'])); + ->willReturnCallback([$this, 'lockSingleDocumentCallback']); $mockTripodUpdate->expects($this->exactly(3)) ->method('getTransactionLog') - ->will($this->returnValue($mockTransactionLog)); + ->willReturn($mockTransactionLog); $mockTripod->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdate)); + ->willReturn($mockTripodUpdate); try { $mockTripod->saveChanges($oG, $nG, 'http://talisaspire.com/'); @@ -314,7 +314,7 @@ public function testTransactionRollbackDuringCreateTransaction() $this->assertDocumentDoesNotHaveProperty(['r' => $subjectTwo, 'c' => 'http://talisaspire.com/'], _LOCKED_FOR_TRANS_TS, $this->tripod); } - public function testTransactionRollbackDuringApplyChanges() + public function testTransactionRollbackDuringApplyChanges(): void { // Save some basic data into the db before we create a transaction to modify it $subjectOne = 'http://example.com/resources/1'; @@ -366,14 +366,14 @@ public function testTransactionRollbackDuringApplyChanges() ->getMock(); $mockTripodUpdate->expects($this->exactly(1)) ->method('generateTransactionId') - ->will($this->returnValue($mockTransactionId)); + ->willReturn($mockTransactionId); $mockTripodUpdate->expects($this->exactly(2)) ->method('lockSingleDocument') - ->will($this->returnCallback([$this, 'lockSingleDocumentCallback'])); - $mockTripodUpdate->expects($this->once())->method('applyChangeSet')->will($this->throwException(new Exception('Exception throw by mock test during applychangeset'))); + ->willReturnCallback([$this, 'lockSingleDocumentCallback']); + $mockTripodUpdate->expects($this->once())->method('applyChangeSet')->willThrowException(new Exception('Exception throw by mock test during applychangeset')); $mockTripod->expects($this->atLeastOnce()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdate)); + ->willReturn($mockTripodUpdate); $mockTripod->setTransactionLog($this->tripodTransactionLog); @@ -401,7 +401,6 @@ public function testTransactionRollbackDuringApplyChanges() $this->assertDocumentDoesNotHaveProperty(['r' => $subjectTwo, 'c' => 'http://talisaspire.com/'], _LOCKED_FOR_TRANS_TS, $this->tripod); $transaction = $mockTripodUpdate->getTransactionLog()->getTransaction($mockTransactionId); - $this->assertNotNull($transaction); $this->assertEquals('Exception throw by mock test during applychangeset', $transaction['error']['reason']); $this->assertEquals('failed', $transaction['status']); } @@ -433,13 +432,9 @@ public function lockSingleDocumentCauseFailureCallback($s, $transactionId, $cont * This is a private method that performs exactly the same operation as Driver::lockSingleDocument, the reason this is duplicated here * is so that we can simulate the correct locking of documents as part of mocking a workflow that will lock a document correctly but not another. * - * @param mixed $s - * @param mixed $transaction_id - * @param mixed $contextAlias - * - * @return array + * @return array|false|null */ - public function lockSingleDocumentCallback($s, $transaction_id, $contextAlias) + public function lockSingleDocumentCallback(?string $s, string $transaction_id, string $contextAlias) { $lCollection = Config::getInstance()->getCollectionForLocks($this->tripod->getStoreName()); $countEntriesInLocksCollection = $lCollection->count(['_id' => [_ID_RESOURCE => $this->labeller->uri_to_alias($s), _ID_CONTEXT => $contextAlias]]); @@ -479,6 +474,7 @@ public function lockSingleDocumentCallback($s, $transaction_id, $contextAlias) if (!$result->isAcknowledged()) { throw new Exception('Failed to create new document with error message'); } + $document = $this->getTripodCollection($this->tripod)->findOne(['_id' => [_ID_RESOURCE => $this->labeller->uri_to_alias($s), _ID_CONTEXT => $contextAlias]]); } catch (Exception $e) { return false; diff --git a/test/unit/mongo/MongoTripodViewsTest.php b/test/unit/mongo/MongoTripodViewsTest.php index 39ed0ab7..dfb5c6fe 100644 --- a/test/unit/mongo/MongoTripodViewsTest.php +++ b/test/unit/mongo/MongoTripodViewsTest.php @@ -1,5 +1,7 @@ tripodViews->getViewForResource('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', 'v_resource_full'); @@ -83,7 +85,8 @@ public function testGenerateView() '_id' => [ _ID_RESOURCE => 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', _ID_CONTEXT => 'http://talisaspire.com/', - 'type' => 'v_resource_full'], + 'type' => 'v_resource_full', + ], 'value' => [ _GRAPHS => [ [ @@ -121,7 +124,11 @@ public function testGenerateView() ]; // get the view direct from mongo $collection = Config::getInstance()->getCollectionForView('tripod_php_testing', 'v_resource_full'); - $actualView = $collection->findOne(['_id' => ['r' => 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', 'c' => 'http://talisaspire.com/', 'type' => 'v_resource_full']]); + $actualView = $collection->findOne(['_id' => [ + 'r' => 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', + 'c' => 'http://talisaspire.com/', + 'type' => 'v_resource_full', + ]]); $this->assertEquals($expectedView['_id'], $actualView['_id']); $this->assertEquals($expectedView['value'], $actualView['value']); $this->assertInstanceOf(UTCDateTime::class, $actualView['_cts']); @@ -130,7 +137,7 @@ public function testGenerateView() /** * Tests view filters removes data, but keeps it in the impact index. */ - public function testGenerateViewWithFilterRemovesFilteredDataButKeepsResourcesInTheImpactIndex() + public function testGenerateViewWithFilterRemovesFilteredDataButKeepsResourcesInTheImpactIndex(): void { // get the view - this should trigger generation $this->tripodViews->getViewForResource('http://talisaspire.com/resources/filter1', 'v_resource_filter1'); @@ -139,7 +146,8 @@ public function testGenerateViewWithFilterRemovesFilteredDataButKeepsResourcesIn '_id' => [ _ID_RESOURCE => 'http://talisaspire.com/resources/filter1', _ID_CONTEXT => 'http://talisaspire.com/', - 'type' => 'v_resource_filter1'], + 'type' => 'v_resource_filter1', + ], 'value' => [ _GRAPHS => [ // This Book should not be included in the view - we are filtering to include only chapters. @@ -190,7 +198,11 @@ public function testGenerateViewWithFilterRemovesFilteredDataButKeepsResourcesIn ]; // get the view direct from mongo $collection = Config::getInstance()->getCollectionForView('tripod_php_testing', 'v_resource_filter1'); - $actualView = $collection->findOne(['_id' => ['r' => 'http://talisaspire.com/resources/filter1', 'c' => 'http://talisaspire.com/', 'type' => 'v_resource_filter1']]); + $actualView = $collection->findOne(['_id' => [ + 'r' => 'http://talisaspire.com/resources/filter1', + 'c' => 'http://talisaspire.com/', + 'type' => 'v_resource_filter1', + ]]); $this->assertEquals($expectedView['_id'], $actualView['_id']); $this->assertEquals($expectedView['value'], $actualView['value']); $this->assertInstanceOf(UTCDateTime::class, $actualView['_cts']); @@ -199,7 +211,7 @@ public function testGenerateViewWithFilterRemovesFilteredDataButKeepsResourcesIn /** * Tests view filter by literal values. */ - public function testGenerateViewWithFilterOnLiteralValue() + public function testGenerateViewWithFilterOnLiteralValue(): void { // get the view - this should trigger generation $this->tripodViews->getViewForResource('http://talisaspire.com/resources/filter1', 'v_resource_filter2'); @@ -208,7 +220,8 @@ public function testGenerateViewWithFilterOnLiteralValue() '_id' => [ _ID_RESOURCE => 'http://talisaspire.com/resources/filter1', _ID_CONTEXT => 'http://talisaspire.com/', - 'type' => 'v_resource_filter2'], + 'type' => 'v_resource_filter2', + ], 'value' => [ _GRAPHS => [ // http://talisaspire.com/works/filter2 has the matching literal @@ -262,7 +275,7 @@ public function testGenerateViewWithFilterOnLiteralValue() /** * Test data removed from view by filter is included in view after update and regeneration. */ - public function testGenerateViewCorrectlyAfterUpdateAffectsFilter() + public function testGenerateViewCorrectlyAfterUpdateAffectsFilter(): void { // get the view - this should trigger generation $this->tripodViews->getViewForResource('http://talisaspire.com/resources/filter1', 'v_resource_filter1'); @@ -271,7 +284,8 @@ public function testGenerateViewCorrectlyAfterUpdateAffectsFilter() '_id' => [ _ID_RESOURCE => 'http://talisaspire.com/resources/filter1', _ID_CONTEXT => 'http://talisaspire.com/', - 'type' => 'v_resource_filter1'], + 'type' => 'v_resource_filter1', + ], 'value' => [ _GRAPHS => [ // This Book should not be included in the view - we are filtering to include only chapters. @@ -330,6 +344,7 @@ public function testGenerateViewCorrectlyAfterUpdateAffectsFilter() // Modify http://talisaspire.com/works/filter1 so that it is a Chapter (included in the view) not a Book (excluded from the view) $oldGraph = new ExtendedGraph(); $oldGraph->add_resource_triple('http://talisaspire.com/works/filter1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'http://purl.org/ontology/bibo/Book'); + $newGraph = new ExtendedGraph(); $newGraph->add_resource_triple('http://talisaspire.com/works/filter1', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'http://purl.org/ontology/bibo/Chapter'); @@ -341,7 +356,8 @@ public function testGenerateViewCorrectlyAfterUpdateAffectsFilter() '_id' => [ _ID_RESOURCE => 'http://talisaspire.com/resources/filter1', _ID_CONTEXT => 'http://talisaspire.com/', - 'type' => 'v_resource_filter1'], + 'type' => 'v_resource_filter1', + ], 'value' => [ _GRAPHS => [ // This work is now included as it's type has changed to Chapter @@ -390,7 +406,11 @@ public function testGenerateViewCorrectlyAfterUpdateAffectsFilter() ], ]; - $updatedView = $collection->findOne(['_id' => ['r' => 'http://talisaspire.com/resources/filter1', 'c' => 'http://talisaspire.com/', 'type' => 'v_resource_filter1']]); + $updatedView = $collection->findOne(['_id' => [ + 'r' => 'http://talisaspire.com/resources/filter1', + 'c' => 'http://talisaspire.com/', + 'type' => 'v_resource_filter1', + ]]); $this->assertEquals($expectedUpdatedView['_id'], $updatedView['_id']); $this->assertEquals($expectedUpdatedView['value'], $updatedView['value']); $this->assertInstanceOf(UTCDateTime::class, $updatedView['_cts']); @@ -399,7 +419,7 @@ public function testGenerateViewCorrectlyAfterUpdateAffectsFilter() /** * Test including an rdf sequence in a view. */ - public function testGenerateViewContainingRdfSequence() + public function testGenerateViewContainingRdfSequence(): void { // get the view - this should trigger generation $this->tripodViews->getViewForResource('http://talisaspire.com/resources/filter1', 'v_resource_rdfsequence'); @@ -408,7 +428,8 @@ public function testGenerateViewContainingRdfSequence() '_id' => [ _ID_RESOURCE => 'http://talisaspire.com/resources/filter1', _ID_CONTEXT => 'http://talisaspire.com/', - 'type' => 'v_resource_rdfsequence'], + 'type' => 'v_resource_rdfsequence', + ], 'value' => [ _GRAPHS => [ [ @@ -470,14 +491,14 @@ public function testGenerateViewContainingRdfSequence() $this->assertInstanceOf(UTCDateTime::class, $actualView['_cts']); } - public function testGenerateViewWithTTL() + public function testGenerateViewWithTTL(): void { $expiryDate = DateUtil::getMongoDate((time() + 300) * 1000); $mockTripodViews = $this->getMockBuilder(Views::class) ->onlyMethods(['getExpirySecFromNow']) ->setConstructorArgs($this->viewsConstParams) ->getMock(); - $mockTripodViews->expects($this->once())->method('getExpirySecFromNow')->with(300)->will($this->returnValue(time() + 300)); + $mockTripodViews->expects($this->once())->method('getExpirySecFromNow')->with(300)->willReturn(time() + 300); $mockTripodViews->getViewForResource('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', 'v_resource_full_ttl'); @@ -486,7 +507,8 @@ public function testGenerateViewWithTTL() '_id' => [ 'r' => 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', 'c' => 'http://talisaspire.com/', - 'type' => 'v_resource_full_ttl'], + 'type' => 'v_resource_full_ttl', + ], 'value' => [ _GRAPHS => [ [ @@ -516,13 +538,17 @@ public function testGenerateViewWithTTL() ], ]; // get the view direct from mongo - $actualView = Config::getInstance()->getCollectionForView('tripod_php_testing', 'v_resource_full_ttl')->findOne(['_id' => ['r' => 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', 'c' => 'http://talisaspire.com/', 'type' => 'v_resource_full_ttl']]); + $actualView = Config::getInstance()->getCollectionForView('tripod_php_testing', 'v_resource_full_ttl')->findOne(['_id' => [ + 'r' => 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', + 'c' => 'http://talisaspire.com/', + 'type' => 'v_resource_full_ttl', + ]]); $this->assertEquals($expectedView['_id'], $actualView['_id']); $this->assertEquals($expectedView['value'], $actualView['value']); $this->assertInstanceOf(UTCDateTime::class, $actualView['_cts']); } - public function testNonExpiringViewWithNegativeTTL() + public function testNonExpiringViewWithNegativeTTL(): void { $views = new Views( $this->viewsConstParams[0], @@ -650,23 +676,23 @@ public function testNonExpiringViewWithNegativeTTL() * * Depending on the order the mongodriver selects data */ - public function testViewGenerationMaxJoinsObjectsMatchPredicates() + public function testViewGenerationMaxJoinsObjectsMatchPredicates(): void { // get the view $graph = $this->tripodViews->getViewForResource('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', 'v_resource_to_single_source'); foreach ($graph->get_resource_triple_values('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', 'http://purl.org/dc/terms/source') as $object) { - $this->assertFalse($graph->get_subject_subgraph($object)->is_empty(), "Subgraph for {$object} should not be empty, should have been followed as join"); + $this->assertFalse($graph->get_subject_subgraph($object)->is_empty(), sprintf('Subgraph for %s should not be empty, should have been followed as join', $object)); } } - public function testTTLViewIsRegeneratedOnFetch() + public function testTTLViewIsRegeneratedOnFetch(): void { // make mock return expiry date in past... $mockTripodViews = $this->getMockBuilder(Views::class) ->onlyMethods(['getExpirySecFromNow']) ->setConstructorArgs($this->viewsConstParams) ->getMock(); - $mockTripodViews->expects($this->once())->method('getExpirySecFromNow')->with(300)->will($this->returnValue(time() - 300)); + $mockTripodViews->expects($this->once())->method('getExpirySecFromNow')->with(300)->willReturn(time() - 300); $mockTripodViews->generateView('v_resource_full_ttl', 'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA'); @@ -680,7 +706,7 @@ public function testTTLViewIsRegeneratedOnFetch() $mockTripodViews2->getViewForResource('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA', 'v_resource_full_ttl'); } - public function testGenerateViewWithCountAggregate() + public function testGenerateViewWithCountAggregate(): void { $expiryDate = DateUtil::getMongoDate((time() + 300) * 1000); @@ -688,7 +714,7 @@ public function testGenerateViewWithCountAggregate() ->onlyMethods(['getExpirySecFromNow']) ->setConstructorArgs($this->viewsConstParams) ->getMock(); - $mockTripodViews->expects($this->once())->method('getExpirySecFromNow')->with(300)->will($this->returnValue(time() + 300)); + $mockTripodViews->expects($this->once())->method('getExpirySecFromNow')->with(300)->willReturn(time() + 300); $mockTripodViews->getViewForResource('http://talisaspire.com/works/4d101f63c10a6', 'v_counts'); @@ -696,7 +722,8 @@ public function testGenerateViewWithCountAggregate() '_id' => [ 'r' => 'http://talisaspire.com/works/4d101f63c10a6', 'c' => 'http://talisaspire.com/', - 'type' => 'v_counts'], + 'type' => 'v_counts', + ], 'value' => [ _GRAPHS => [ [ @@ -736,13 +763,17 @@ public function testGenerateViewWithCountAggregate() ], ]; - $actualView = Config::getInstance()->getCollectionForView('tripod_php_testing', 'v_counts')->findOne(['_id' => ['r' => 'http://talisaspire.com/works/4d101f63c10a6', 'c' => 'http://talisaspire.com/', 'type' => 'v_counts']]); + $actualView = Config::getInstance()->getCollectionForView('tripod_php_testing', 'v_counts')->findOne(['_id' => [ + 'r' => 'http://talisaspire.com/works/4d101f63c10a6', + 'c' => 'http://talisaspire.com/', + 'type' => 'v_counts', + ]]); $this->assertEquals($expectedView['_id'], $actualView['_id']); $this->assertEquals($expectedView['value'], $actualView['value']); $this->assertInstanceOf(UTCDateTime::class, $actualView['_cts']); } - public function testGetViewWithNamespaces() + public function testGetViewWithNamespaces(): void { $g = $this->tripodViews->getViewForResource('baseData:1', 'v_work_see_also', 'baseData:DefaultGraph'); $this->assertFalse($g->is_empty(), 'Graph should not be empty'); @@ -768,13 +799,13 @@ public function testGetViewWithNamespaces() $this->assertEquals($g2->to_ntriples(), $g5->to_ntriples(), 'View requested with subject/context qnamed should be equal to that with only context namespaced'); } - public function testGenerateViewsForResourcesOfTypeWithNamespace() + public function testGenerateViewsForResourcesOfTypeWithNamespace(): void { $mockTripodViews = $this->getMockBuilder(Views::class) ->onlyMethods(['generateView']) ->setConstructorArgs($this->viewsConstParams) ->getMock(); - $mockTripodViews->expects($this->atLeastOnce())->method('generateView')->will($this->returnValue(['ok' => true])); + $mockTripodViews->expects($this->atLeastOnce())->method('generateView')->willReturn(['ok' => true]); // spec is namespaced, acorn:Work, can it resolve? $mockTripodViews->generateViewsForResourcesOfType('http://talisaspire.com/schema#Work'); @@ -783,7 +814,7 @@ public function testGenerateViewsForResourcesOfTypeWithNamespace() ->onlyMethods(['generateView']) ->setConstructorArgs($this->viewsConstParams) ->getMock(); - $mockTripodViews->expects($this->atLeastOnce())->method('generateView')->will($this->returnValue(['ok' => true])); + $mockTripodViews->expects($this->atLeastOnce())->method('generateView')->willReturn(['ok' => true]); // spec is fully qualified, http://talisaspire.com/shema#Work2, can it resolve? $mockTripodViews->generateViewsForResourcesOfType('acorn:Work2'); @@ -791,7 +822,7 @@ public function testGenerateViewsForResourcesOfTypeWithNamespace() // todo: more unit tests to cover other view spec/search document properties: condition, maxJoins, followSequence, from - public function testGetViewForResourcesDoesNotInvokeViewGenerationForMissingResources() + public function testGetViewForResourcesDoesNotInvokeViewGenerationForMissingResources(): void { $uri1 = 'http://uri1'; $uri2 = 'http://uri2'; @@ -808,7 +839,7 @@ public function testGetViewForResourcesDoesNotInvokeViewGenerationForMissingReso ], ]; - $returnedGraph = new ExtendedGraph(); + $returnedGraph = new MongoGraph(); $returnedGraph->add_literal_triple($uri1, 'http://somepred', 'someval'); $mockDb = $this->getMockBuilder(Database::class) @@ -824,8 +855,8 @@ public function testGetViewForResourcesDoesNotInvokeViewGenerationForMissingReso ->onlyMethods(['findOne']) ->getMock(); - $mockDb->expects($this->any())->method('selectCollection')->will($this->returnValue($mockColl)); - $mockColl->expects($this->once())->method('findOne')->will($this->returnValue(null)); + $mockDb->method('selectCollection')->willReturn($mockColl); + $mockColl->expects($this->once())->method('findOne')->willReturn(null); $mockConfig = $this->getMockBuilder(TripodTestConfig::class) ->onlyMethods(['getCollectionForCBD', 'getCollectionForView']) @@ -834,12 +865,12 @@ public function testGetViewForResourcesDoesNotInvokeViewGenerationForMissingReso $mockConfig->expects($this->atLeastOnce()) ->method('getCollectionForCBD') ->with('tripod_php_testing', $this->anything(), $this->anything()) - ->will($this->returnValue($mockColl)); + ->willReturn($mockColl); $mockConfig->expects($this->atLeastOnce()) ->method('getCollectionForView') ->with('tripod_php_testing', $this->anything(), $this->anything()) - ->will($this->returnValue($mockViewColl)); + ->willReturn($mockViewColl); $mockConfig->loadConfig(Config::getConfig()); @@ -854,18 +885,18 @@ public function testGetViewForResourcesDoesNotInvokeViewGenerationForMissingReso $mockTripodViews->expects($this->once()) ->method('fetchGraph') ->with($query, MONGO_VIEW, $mockViewColl, null, 101) - ->will($this->returnValue($returnedGraph)); + ->willReturn($returnedGraph); $mockTripodViews->expects($this->atLeastOnce()) ->method('getConfigInstance') - ->will($this->returnValue($mockConfig)); + ->willReturn($mockConfig); $resultGraph = $mockTripodViews->getViewForResources([$uri1, $uri2], $viewType, $context); $this->assertEquals($returnedGraph->to_ntriples(), $resultGraph->to_ntriples()); } - public function testGetViewForResourcesInvokesViewGenerationForMissingResources() + public function testGetViewForResourcesInvokesViewGenerationForMissingResources(): void { $uri1 = 'http://uri1'; $uri2 = 'http://uri2'; @@ -886,8 +917,8 @@ public function testGetViewForResourcesInvokesViewGenerationForMissingResources( ->onlyMethods(['findOne']) ->getMock(); - $mockDb->expects($this->any())->method('selectCollection')->will($this->returnValue($mockColl)); - $mockColl->expects($this->once())->method('findOne')->will($this->returnValue(['_id' => $uri1])); // the actual returned doc is not important, it just has to not be null + $mockDb->method('selectCollection')->willReturn($mockColl); + $mockColl->expects($this->once())->method('findOne')->willReturn(['_id' => $uri1]); // the actual returned doc is not important, it just has to not be null $mockConfig = $this->getMockBuilder(TripodTestConfig::class) ->onlyMethods(['getCollectionForCBD', 'getCollectionForView']) @@ -896,12 +927,12 @@ public function testGetViewForResourcesInvokesViewGenerationForMissingResources( $mockConfig->expects($this->atLeastOnce()) ->method('getCollectionForCBD') ->with('tripod_php_testing', $this->anything(), $this->anything()) - ->will($this->returnValue($mockColl)); + ->willReturn($mockColl); $mockConfig->expects($this->atLeastOnce()) ->method('getCollectionForView') ->with('tripod_php_testing', $this->anything(), $this->anything()) - ->will($this->returnValue($mockViewColl)); + ->willReturn($mockViewColl); $mockConfig->loadConfig(Config::getConfig()); @@ -913,15 +944,15 @@ public function testGetViewForResourcesInvokesViewGenerationForMissingResources( $mockTripodViews->expects($this->once()) ->method('generateView') ->with($viewType, $uri2, $context) - ->will($this->returnValue(['ok' => true])); + ->willReturn(['ok' => true]); $mockTripodViews->expects($this->exactly(2)) ->method('fetchGraph') - ->will($this->returnCallback([$this, 'fetchGraphInGetViewForResourcesCallback'])); + ->willReturnCallback([$this, 'fetchGraphInGetViewForResourcesCallback']); $mockTripodViews->expects($this->atLeastOnce()) ->method('getConfigInstance') - ->will($this->returnValue($mockConfig)); + ->willReturn($mockConfig); $resultGraph = $mockTripodViews->getViewForResources([$uri1, $uri2], $viewType, $context); @@ -932,7 +963,7 @@ public function testGetViewForResourcesInvokesViewGenerationForMissingResources( $this->assertEquals($expectedGraph->to_ntriples(), $resultGraph->to_ntriples()); } - public function testDeletionOfResourceTriggersViewRegeneration() + public function testDeletionOfResourceTriggersViewRegeneration(): void { $context = 'http://talisaspire.com/'; @@ -953,6 +984,7 @@ public function testDeletionOfResourceTriggersViewRegeneration() ); $originalGraph->add_resource_triple($uri1, $labeller->qname_to_uri('dct:isVersionOf'), $uri2); + $tripod = new Driver('CBD_testing', 'tripod_php_testing', ['defaultContext' => $context]); $tripod->saveChanges(new ExtendedGraph(), $originalGraph); @@ -970,7 +1002,9 @@ public function testDeletionOfResourceTriggersViewRegeneration() $subjectsAndPredicatesOfChange = [ $labeller->uri_to_alias($uri1) => [ - 'rdf:type', 'searchterms:topic', 'dct:isVersionOf', + 'rdf:type', + 'searchterms:topic', + 'dct:isVersionOf', ], ]; @@ -1015,12 +1049,12 @@ public function testDeletionOfResourceTriggersViewRegeneration() $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $mockTripod->expects($this->once()) ->method('getComposite') ->with(OP_VIEWS) - ->will($this->returnValue($mockViews)); + ->willReturn($mockViews); $mockTripodUpdates->expects($this->once()) ->method('processSyncOperations') @@ -1090,7 +1124,7 @@ public function testDeletionOfResourceTriggersViewRegeneration() /** * Basically identical to testDeletionOfResourceTriggersViewRegeneration, but focus on $url2, instead. */ - public function testDeletionOfResourceInImpactIndexTriggersViewRegeneration() + public function testDeletionOfResourceInImpactIndexTriggersViewRegeneration(): void { $context = 'http://talisaspire.com/'; @@ -1107,6 +1141,7 @@ public function testDeletionOfResourceInImpactIndexTriggersViewRegeneration() $originalGraph->add_literal_triple($uri2, $labeller->qname_to_uri('dct:subject'), 'Things grouped by no specific criteria'); $originalGraph->add_resource_triple($uri1, $labeller->qname_to_uri('dct:isVersionOf'), $uri2); + $tripod = new Driver('CBD_testing', 'tripod_php_testing', ['defaultContext' => $context]); $tripod->saveChanges(new ExtendedGraph(), $originalGraph); @@ -1118,13 +1153,15 @@ public function testDeletionOfResourceInImpactIndexTriggersViewRegeneration() $subjectsAndPredicatesOfChange = [ $labeller->uri_to_alias($uri2) => [ - 'rdf:type', 'dct:subject', + 'rdf:type', + 'dct:subject', ], ]; $mockTripod = $this->getMockBuilder(Driver::class) ->onlyMethods([ - 'getDataUpdater', 'getComposite', + 'getDataUpdater', + 'getComposite', ]) ->setConstructorArgs([ 'CBD_testing', @@ -1168,12 +1205,12 @@ public function testDeletionOfResourceInImpactIndexTriggersViewRegeneration() $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $mockTripod->expects($this->once()) ->method('getComposite') ->with(OP_VIEWS) - ->will($this->returnValue($mockViews)); + ->willReturn($mockViews); $mockTripodUpdates->expects($this->once()) ->method('processSyncOperations') @@ -1265,7 +1302,7 @@ public function testDeletionOfResourceInImpactIndexTriggersViewRegeneration() * Basically identical to testDeletionOfResourceInImpactIndexTriggersViewRegeneration, but update $url2, rather * than deleting it. */ - public function testUpdateOfResourceInImpactIndexTriggersViewRegeneration() + public function testUpdateOfResourceInImpactIndexTriggersViewRegeneration(): void { $context = 'http://talisaspire.com/'; @@ -1282,6 +1319,7 @@ public function testUpdateOfResourceInImpactIndexTriggersViewRegeneration() $originalGraph->add_literal_triple($uri2, $labeller->qname_to_uri('dct:subject'), 'Things grouped by no specific criteria'); $originalGraph->add_resource_triple($uri1, $labeller->qname_to_uri('dct:isVersionOf'), $uri2); + $tripod = new Driver('CBD_testing', 'tripod_php_testing', ['defaultContext' => $context]); $tripod->saveChanges(new ExtendedGraph(), $originalGraph); @@ -1297,7 +1335,8 @@ public function testUpdateOfResourceInImpactIndexTriggersViewRegeneration() $mockTripod = $this->getMockBuilder(Driver::class) ->onlyMethods([ - 'getDataUpdater', 'getComposite', + 'getDataUpdater', + 'getComposite', ]) ->setConstructorArgs([ 'CBD_testing', @@ -1341,12 +1380,12 @@ public function testUpdateOfResourceInImpactIndexTriggersViewRegeneration() $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $mockTripod->expects($this->once()) ->method('getComposite') ->with(OP_VIEWS) - ->will($this->returnValue($mockViews)); + ->willReturn($mockViews); $mockTripodUpdates->expects($this->once()) ->method('processSyncOperations') @@ -1400,6 +1439,7 @@ public function testUpdateOfResourceInImpactIndexTriggersViewRegeneration() $newGraph = $originalGraph->get_subject_subgraph($uri2); $newGraph->replace_literal_triple($uri2, $labeller->qname_to_uri('dct:subject'), 'Things grouped by no specific criteria', 'Grab bag'); + $mockTripod->saveChanges($originalGraph->get_subject_subgraph($uri2), $newGraph); // Walk through the processSyncOperations process manually for views @@ -1439,7 +1479,7 @@ public function testUpdateOfResourceInImpactIndexTriggersViewRegeneration() /** * Similar to testDeletionOfResourceTriggersViewRegeneration except $url1 is updated, rather than deleted. */ - public function testUpdateOfResourceTriggersViewRegeneration() + public function testUpdateOfResourceTriggersViewRegeneration(): void { $context = 'http://talisaspire.com/'; @@ -1456,6 +1496,7 @@ public function testUpdateOfResourceTriggersViewRegeneration() $originalGraph->add_literal_triple($uri2, $labeller->qname_to_uri('dct:subject'), 'Things grouped by no specific criteria'); $originalGraph->add_resource_triple($uri1, $labeller->qname_to_uri('dct:isVersionOf'), $uri2); + $tripod = new Driver('CBD_testing', 'tripod_php_testing', ['defaultContext' => $context]); $tripod->saveChanges(new ExtendedGraph(), $originalGraph); @@ -1471,7 +1512,8 @@ public function testUpdateOfResourceTriggersViewRegeneration() $mockTripod = $this->getMockBuilder(Driver::class) ->onlyMethods([ - 'getDataUpdater', 'getComposite', + 'getDataUpdater', + 'getComposite', ]) ->setConstructorArgs([ 'CBD_testing', @@ -1515,12 +1557,12 @@ public function testUpdateOfResourceTriggersViewRegeneration() $mockTripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $mockTripod->expects($this->once()) ->method('getComposite') ->with(OP_VIEWS) - ->will($this->returnValue($mockViews)); + ->willReturn($mockViews); $mockTripodUpdates->expects($this->once()) ->method('processSyncOperations') @@ -1574,6 +1616,7 @@ public function testUpdateOfResourceTriggersViewRegeneration() $newGraph = $originalGraph->get_subject_subgraph($uri1); $newGraph->add_literal_triple($uri1, $labeller->qname_to_uri('dct:title'), 'Title of Resource'); + $mockTripod->saveChanges($originalGraph->get_subject_subgraph($uri1), $newGraph); // Walk through the processSyncOperations process manually for views @@ -1613,7 +1656,7 @@ public function testUpdateOfResourceTriggersViewRegeneration() * Test that a change to a resource that isn't covered by a viewspec or in an impact index still triggers the discover * impacted subjects operation and returns nothing. */ - public function testResourceUpdateNotCoveredBySpecStillTriggersOperations() + public function testResourceUpdateNotCoveredBySpecStillTriggersOperations(): void { $context = 'http://talisaspire.com/'; @@ -1624,7 +1667,7 @@ public function testResourceUpdateNotCoveredBySpecStillTriggersOperations() $uri1 = 'http://example.com/resources/' . uniqid(); $originalGraph->add_resource_triple($uri1, RDF_TYPE, $labeller->qname_to_uri('bibo:Document')); $originalGraph->add_literal_triple($uri1, $labeller->qname_to_uri('dct:title'), 'How to speak American like a native'); - $originalGraph->add_literal_triple($uri1, $labeller->qname_to_uri('dct:subject'), 'Languages -- \'Murrican'); + $originalGraph->add_literal_triple($uri1, $labeller->qname_to_uri('dct:subject'), "Languages -- 'Murrican"); $originalSubjectsAndPredicatesOfChange = [ $labeller->uri_to_alias($uri1) => ['rdf:type', 'dct:title', 'dct:subject'], @@ -1636,7 +1679,8 @@ public function testResourceUpdateNotCoveredBySpecStillTriggersOperations() $mockTripod = $this->getMockBuilder(Driver::class) ->onlyMethods([ - 'getDataUpdater', 'getComposite', + 'getDataUpdater', + 'getComposite', ]) ->setConstructorArgs([ 'CBD_testing', @@ -1680,12 +1724,12 @@ public function testResourceUpdateNotCoveredBySpecStillTriggersOperations() $mockTripod->expects($this->exactly(2)) ->method('getDataUpdater') - ->will($this->returnValue($mockTripodUpdates)); + ->willReturn($mockTripodUpdates); $mockTripod->expects($this->exactly(2)) ->method('getComposite') ->with(OP_VIEWS) - ->will($this->returnValue($mockViews)); + ->willReturn($mockViews); $mockTripodUpdates->expects($this->exactly(2)) ->method('processSyncOperations') @@ -1726,7 +1770,7 @@ public function testResourceUpdateNotCoveredBySpecStillTriggersOperations() $this->assertEmpty($impactedSubjects); $newGraph = $originalGraph->get_subject_subgraph($uri1); - $newGraph->replace_literal_triple($uri1, $labeller->qname_to_uri('dct:subject'), 'Languages -- \'Murrican', 'Languages -- English, American'); + $newGraph->replace_literal_triple($uri1, $labeller->qname_to_uri('dct:subject'), "Languages -- 'Murrican", 'Languages -- English, American'); $mockTripod->saveChanges($originalGraph, $newGraph); @@ -1742,7 +1786,7 @@ public function testResourceUpdateNotCoveredBySpecStillTriggersOperations() * Save several new resources in a single operation. Only one of the resources has a type that is applicable based on specifications, * therefore only one ImpactedSubject should be created. */ - public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject() + public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject(): void { $tripod = $this->getMockBuilder(Driver::class) ->onlyMethods(['getDataUpdater']) @@ -1779,7 +1823,7 @@ public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject() $tripod->expects($this->once()) ->method('getDataUpdater') - ->will($this->returnValue($tripodUpdates)); + ->willReturn($tripodUpdates); // first lets add a book, which should trigger a search doc, view and table gen for a single item $g = new MongoGraph(); @@ -1805,6 +1849,7 @@ public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject() $g->add_literal_triple($newSubjectUri3, $g->qname_to_uri('dct:title'), 'This is yet another new resource'); $g->add_literal_triple($newSubjectUri3, $g->qname_to_uri('dct:subject'), 'art'); $g->add_literal_triple($newSubjectUri3, $g->qname_to_uri('dct:subject'), 'design'); + $subjectsAndPredicatesOfChange = [ $newSubjectUri1 => ['rdf:type', 'dct:creator', 'dct:title', 'dct:subject'], $newSubjectUri2 => ['rdf:type', 'dct:creator', 'dct:title', 'dct:subject'], @@ -1831,7 +1876,7 @@ public function testSavingMultipleNewEntitiesResultsInOneImpactedSubject() $this->assertEquals($expectedImpactedSubjects, $impactedSubjects); } - public function testSavingToAPreviouslyEmptySeqeunceUpdatesView() + public function testSavingToAPreviouslyEmptySeqeunceUpdatesView(): void { // create a tripod with views sync $tripod = new Driver('CBD_testing', 'tripod_php_testing', [ @@ -1855,7 +1900,7 @@ public function testSavingToAPreviouslyEmptySeqeunceUpdatesView() $this->assertTrue($view->has_triples_about('http://basedata.com/b/sequence123')); } - public function testSavingToAPreviouslyEmptyJoinUpdatesView() + public function testSavingToAPreviouslyEmptyJoinUpdatesView(): void { // create a tripod with views sync $tripod = new Driver('CBD_testing', 'tripod_php_testing', [ @@ -1879,10 +1924,7 @@ public function testSavingToAPreviouslyEmptyJoinUpdatesView() $this->assertTrue($view->has_triples_about('http://schemas.talis.com/2005/user/schema#xyz')); } - /** - * @return ExtendedGraph - */ - public function fetchGraphInGetViewForResourcesCallback() + public function fetchGraphInGetViewForResourcesCallback(...$args): MongoGraph { $uri1 = 'http://uri1'; $uri2 = 'http://uri2'; @@ -1893,32 +1935,29 @@ public function fetchGraphInGetViewForResourcesCallback() $query1 = ['_id' => ['$in' => [['r' => $uri1, 'c' => $context, 'type' => $viewType], ['r' => $uri2, 'c' => $context, 'type' => $viewType]]]]; $query2 = ['_id' => ['$in' => [['r' => $uri2, 'c' => $context, 'type' => $viewType]]]]; - $returnedGraph1 = new ExtendedGraph(); + $returnedGraph1 = new MongoGraph(); $returnedGraph1->add_literal_triple($uri1, 'http://somepred', 'someval'); - $returnedGraph2 = new ExtendedGraph(); + $returnedGraph2 = new MongoGraph(); $returnedGraph2->add_literal_triple($uri2, 'http://somepred', 'someval'); - - $args = func_get_args(); if ($args[0] == $query1) { return $returnedGraph1; } + if ($args[0] == $query2) { return $returnedGraph2; } + $this->fail(); } - public function testCursorNoExceptions() + public function testCursorNoExceptions(): void { $uri1 = 'http://uri1'; $viewType = 'someView'; $context = 'http://someContext'; - $returnedGraph = new ExtendedGraph(); - $returnedGraph->add_literal_triple($uri1, 'http://somepred', 'someval'); - $mockDb = $this->getMockBuilder(Database::class) ->disableOriginalConstructor() ->onlyMethods(['selectCollection']) @@ -1935,10 +1974,10 @@ public function testCursorNoExceptions() ->onlyMethods(['rewind']) ->getMock(); - $mockViewColl->expects($this->once())->method('find')->will($this->returnValue($mockCursor)); + $mockViewColl->expects($this->once())->method('find')->willReturn($mockCursor); - $mockDb->expects($this->any())->method('selectCollection')->will($this->returnValue($mockColl)); - $mockColl->expects($this->once())->method('findOne')->will($this->returnValue(null)); + $mockDb->method('selectCollection')->willReturn($mockColl); + $mockColl->expects($this->once())->method('findOne')->willReturn(null); $mockConfig = $this->getMockBuilder(TripodTestConfig::class) ->onlyMethods(['getCollectionForCBD', 'getCollectionForView']) @@ -1947,12 +1986,12 @@ public function testCursorNoExceptions() $mockConfig->expects($this->atLeastOnce()) ->method('getCollectionForCBD') ->with('tripod_php_testing', $this->anything(), $this->anything()) - ->will($this->returnValue($mockColl)); + ->willReturn($mockColl); $mockConfig->expects($this->atLeastOnce()) ->method('getCollectionForView') ->with('tripod_php_testing', $this->anything(), $this->anything()) - ->will($this->returnValue($mockViewColl)); + ->willReturn($mockViewColl); $mockConfig->loadConfig(Config::getConfig()); @@ -1966,21 +2005,18 @@ public function testCursorNoExceptions() $mockTripodViews->expects($this->atLeastOnce()) ->method('getConfigInstance') - ->will($this->returnValue($mockConfig)); + ->willReturn($mockConfig); $mockTripodViews->getViewForResources([$uri1], $viewType, $context); } - public function testCursorExceptionThrown() + public function testCursorExceptionThrown(): void { $uri1 = 'http://uri1'; $viewType = 'someView'; $context = 'http://someContext'; - $returnedGraph = new ExtendedGraph(); - $returnedGraph->add_literal_triple($uri1, 'http://somepred', 'someval'); - $mockDb = $this->getMockBuilder(Database::class) ->disableOriginalConstructor() ->onlyMethods(['selectCollection']) @@ -1997,10 +2033,10 @@ public function testCursorExceptionThrown() ->onlyMethods(['rewind']) ->getMock(); - $mockCursor->expects($this->exactly(30))->method('rewind')->will($this->throwException(new Exception('Exception thrown when cursoring to Mongo'))); - $mockViewColl->expects($this->once())->method('find')->will($this->returnValue($mockCursor)); + $mockCursor->expects($this->exactly(30))->method('rewind')->willThrowException(new Exception('Exception thrown when cursoring to Mongo')); + $mockViewColl->expects($this->once())->method('find')->willReturn($mockCursor); - $mockDb->expects($this->any())->method('selectCollection')->will($this->returnValue($mockColl)); + $mockDb->method('selectCollection')->willReturn($mockColl); $mockColl->expects($this->never())->method('findOne'); $mockConfig = $this->getMockBuilder(TripodTestConfig::class) @@ -2013,7 +2049,7 @@ public function testCursorExceptionThrown() $mockConfig->expects($this->atLeastOnce()) ->method('getCollectionForView') ->with('tripod_php_testing', $this->anything(), $this->anything()) - ->will($this->returnValue($mockViewColl)); + ->willReturn($mockViewColl); $mockConfig->loadConfig(Config::getConfig()); @@ -2027,23 +2063,20 @@ public function testCursorExceptionThrown() $mockTripodViews->expects($this->atLeastOnce()) ->method('getConfigInstance') - ->will($this->returnValue($mockConfig)); + ->willReturn($mockConfig); $this->expectException(Exception::class); $this->expectExceptionMessage('Exception thrown when cursoring to Mongo'); $mockTripodViews->getViewForResources([$uri1], $viewType, $context); } - public function testCursorNoExceptionThrownWhenCursorThrowsSomeExceptions() + public function testCursorNoExceptionThrownWhenCursorThrowsSomeExceptions(): void { $uri1 = 'http://uri1'; $viewType = 'someView'; $context = 'http://someContext'; - $returnedGraph = new ExtendedGraph(); - $returnedGraph->add_literal_triple($uri1, 'http://somepred', 'someval'); - $mockDb = $this->getMockBuilder(Database::class) ->disableOriginalConstructor() ->onlyMethods(['selectCollection']) @@ -2061,19 +2094,18 @@ public function testCursorNoExceptionThrownWhenCursorThrowsSomeExceptions() ->getMock(); $mockCursor->expects($this->exactly(5)) - ->method('rewind') - ->will($this->onConsecutiveCalls( + ->method('rewind')->willReturnOnConsecutiveCalls( $this->throwException(new Exception('Exception thrown when cursoring to Mongo')), $this->throwException(new Exception('Exception thrown when cursoring to Mongo')), $this->throwException(new Exception('Exception thrown when cursoring to Mongo')), $this->throwException(new Exception('Exception thrown when cursoring to Mongo')), $this->returnValue($mockCursor) - )); + ); - $mockViewColl->expects($this->once())->method('find')->will($this->returnValue($mockCursor)); + $mockViewColl->expects($this->once())->method('find')->willReturn($mockCursor); - $mockDb->expects($this->any())->method('selectCollection')->will($this->returnValue($mockColl)); - $mockColl->expects($this->once())->method('findOne')->will($this->returnValue(null)); + $mockDb->method('selectCollection')->willReturn($mockColl); + $mockColl->expects($this->once())->method('findOne')->willReturn(null); $mockConfig = $this->getMockBuilder(TripodTestConfig::class) ->onlyMethods(['getCollectionForCBD', 'getCollectionForView']) @@ -2082,12 +2114,12 @@ public function testCursorNoExceptionThrownWhenCursorThrowsSomeExceptions() $mockConfig->expects($this->atLeastOnce()) ->method('getCollectionForCBD') ->with('tripod_php_testing', $this->anything(), $this->anything()) - ->will($this->returnValue($mockColl)); + ->willReturn($mockColl); $mockConfig->expects($this->atLeastOnce()) ->method('getCollectionForView') ->with('tripod_php_testing', $this->anything(), $this->anything()) - ->will($this->returnValue($mockViewColl)); + ->willReturn($mockViewColl); $mockConfig->loadConfig(Config::getConfig()); @@ -2101,12 +2133,12 @@ public function testCursorNoExceptionThrownWhenCursorThrowsSomeExceptions() $mockTripodViews->expects($this->atLeastOnce()) ->method('getConfigInstance') - ->will($this->returnValue($mockConfig)); + ->willReturn($mockConfig); $mockTripodViews->getViewForResources([$uri1], $viewType, $context); } - public function testCountViews() + public function testCountViews(): void { $collection = $this->getMockBuilder(Collection::class) ->setConstructorArgs([new Manager(), 'db', 'coll']) @@ -2120,19 +2152,19 @@ public function testCountViews() $views->expects($this->once()) ->method('getCollectionForViewSpec') ->with('v_some_spec') - ->will($this->returnValue($collection)); + ->willReturn($collection); $collection->expects($this->once()) ->method('count') ->with(['_id.type' => 'v_some_spec']) - ->will($this->returnValue(101)); + ->willReturn(101); $this->assertEquals(101, $views->count('v_some_spec')); } - public function testCountViewsWithFilters() + public function testCountViewsWithFilters(): void { - $filters = ['_cts' => ['$lte' => new UTCDateTime(null)]]; + $filters = ['_cts' => ['$lte' => new UTCDateTime()]]; $query = array_merge(['_id.type' => 'v_some_spec'], $filters); $collection = $this->getMockBuilder(Collection::class) ->setConstructorArgs([new Manager(), 'db', 'coll']) @@ -2146,17 +2178,17 @@ public function testCountViewsWithFilters() $views->expects($this->once()) ->method('getCollectionForViewSpec') ->with('v_some_spec') - ->will($this->returnValue($collection)); + ->willReturn($collection); $collection->expects($this->once()) ->method('count') ->with($query) - ->will($this->returnValue(101)); + ->willReturn(101); $this->assertEquals(101, $views->count('v_some_spec', $filters)); } - public function testDeleteViewsByViewId() + public function testDeleteViewsByViewId(): void { $collection = $this->getMockBuilder(Collection::class) ->setConstructorArgs([new Manager(), 'db', 'coll']) @@ -2170,7 +2202,7 @@ public function testDeleteViewsByViewId() $deleteResult->expects($this->once()) ->method('getDeletedCount') - ->will($this->returnValue(30)); + ->willReturn(30); $views = $this->getMockBuilder(Views::class) ->onlyMethods(['getCollectionForViewSpec']) @@ -2180,19 +2212,19 @@ public function testDeleteViewsByViewId() $views->expects($this->once()) ->method('getCollectionForViewSpec') ->with('v_resource_full') - ->will($this->returnValue($collection)); + ->willReturn($collection); $collection->expects($this->once()) ->method('deleteMany') ->with(['_id.type' => 'v_resource_full']) - ->will($this->returnValue($deleteResult)); + ->willReturn($deleteResult); $this->assertEquals(30, $views->deleteViewsByViewId('v_resource_full')); } - public function testDeleteViewsByViewIdWithTimestamp() + public function testDeleteViewsByViewIdWithTimestamp(): void { - $timestamp = new UTCDateTime(null); + $timestamp = new UTCDateTime(); $query = [ '_id.type' => 'v_resource_full', @@ -2213,7 +2245,7 @@ public function testDeleteViewsByViewIdWithTimestamp() $deleteResult->expects($this->once()) ->method('getDeletedCount') - ->will($this->returnValue(30)); + ->willReturn(30); $views = $this->getMockBuilder(Views::class) ->onlyMethods(['getCollectionForViewSpec']) @@ -2223,17 +2255,17 @@ public function testDeleteViewsByViewIdWithTimestamp() $views->expects($this->once()) ->method('getCollectionForViewSpec') ->with('v_resource_full') - ->will($this->returnValue($collection)); + ->willReturn($collection); $collection->expects($this->once()) ->method('deleteMany') ->with($query) - ->will($this->returnValue($deleteResult)); + ->willReturn($deleteResult); $this->assertEquals(30, $views->deleteViewsByViewId('v_resource_full', $timestamp)); } - public function testBatchViewGeneration() + public function testBatchViewGeneration(): void { $count = 234; $docs = []; diff --git a/test/unit/mongo/ResqueJobTestBase.php b/test/unit/mongo/ResqueJobTestBase.php index 5fe2d5c6..2c5d0c0e 100644 --- a/test/unit/mongo/ResqueJobTestBase.php +++ b/test/unit/mongo/ResqueJobTestBase.php @@ -1,5 +1,7 @@ getMock(); $mockJob->expects($this->atLeastOnce()) ->method('getInstance') - ->will($this->returnValue($job)); - $mockJob->expects($this->any()) + ->willReturn($job); + $mockJob ->method('getArguments') - ->will($this->returnValue($job->args)); + ->willReturn($job->args); $mockJob->perform(); } } diff --git a/test/unit/mongo/TestConfigGenerator.php b/test/unit/mongo/TestConfigGenerator.php index 4a1a19b1..141c85ea 100644 --- a/test/unit/mongo/TestConfigGenerator.php +++ b/test/unit/mongo/TestConfigGenerator.php @@ -1,6 +1,7 @@ + */ + public function serialize(): array { return ['class' => get_class($this), 'filename' => $this->fileName]; } - public static function deserialize(array $config) + public static function deserialize(array $config): IConfigInstance { $instance = new self(); $instance->fileName = $config['filename']; + $cfg = json_decode(file_get_contents($config['filename']), true); $instance->loadConfig($cfg); diff --git a/test/unit/mongo/TestJobBase.php b/test/unit/mongo/TestJobBase.php index 9cdb9bbb..5734d923 100644 --- a/test/unit/mongo/TestJobBase.php +++ b/test/unit/mongo/TestJobBase.php @@ -1,5 +1,6 @@ ['r' => 'http://serials.talisaspire.com/issn/0893-0465', 'c' => 'http://talisaspire.com/'], 'foaf:page' => [ [ - 'u' => 'http://www.ingentaconnect.com/content/bpl/ciso'], + 'u' => 'http://www.ingentaconnect.com/content/bpl/ciso', + ], [ - 'u' => 'http://onlinelibrary.wiley.com/journal/10.1111/(ISSN)1548-744X'], + 'u' => 'http://onlinelibrary.wiley.com/journal/10.1111/(ISSN)1548-744X', + ], ], 'rdf:type' => [ 'u' => 'bibo:Journal', @@ -29,7 +33,7 @@ public function testGetTArrayAbout() 'l' => '1548-774X', ], ]; - $this->assertEquals($expectedDoc, $tu->getTArrayAbout('http://serials.talisaspire.com/issn/0893-0465', $triples, 'http://talisaspire.com/')); + $this->assertSame($expectedDoc, $tu->getTArrayAbout('http://serials.talisaspire.com/issn/0893-0465', $triples, 'http://talisaspire.com/')); } // todo: add triples test