diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 9e5ea6a9..8507df8e 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -12,7 +12,9 @@ jobs: fail-fast: false matrix: php: - - '7.4' + - '8.2' + - '8.3' + - '8.4' env: WP_ENV_PHP_VERSION: ${{ matrix.php }} @@ -25,9 +27,7 @@ jobs: run: npm install - name: Setup Environment - run: | - rm composer.lock - npm run setup + run: npm run setup - name: Test run: npm run test diff --git a/.gitignore b/.gitignore index 82f9b225..7e15528b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ vendor coverage node_modules +.phpunit.result.cache diff --git a/.wp-env.json b/.wp-env.json index aaf14faf..ce17c31e 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -1,7 +1,6 @@ { - "phpVersion": "7.4", + "phpVersion": "8.2", "plugins": [ - ".", - "https://downloads.wordpress.org/plugin/posts-to-posts.latest-stable.zip" + "." ] } diff --git a/bin/generate-golden-docker.sh b/bin/generate-golden-docker.sh new file mode 100755 index 00000000..265d988f --- /dev/null +++ b/bin/generate-golden-docker.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# +# Regenerate the golden-master snapshots on PHP 7.4 — the last PHP version the old +# parser (phpdocumentor/reflection ~3.0 + nikic/php-parser 1.x) runs on cleanly. +# On PHP 8.x that stack emits a flood of deprecations/warnings and produces +# unreliable output, so the baseline MUST be captured on 7.4. +# +# Reuses the host's already-installed vendor/ (pure PHP, version-agnostic), so no +# Composer or build tooling is needed inside the container. +# +# Prereq: composer install --no-dev # installs the OLD locked parser stack +# Usage: bin/generate-golden-docker.sh +# +set -euo pipefail +cd "$(dirname "$0")/.." + +if [ ! -f vendor/phpdocumentor/reflection/composer.json ]; then + echo "ERROR: old parser not installed. Run: composer install --no-dev" >&2 + exit 1 +fi + +exec docker run --rm \ + -u "$(id -u):$(id -g)" -e HOME=/tmp \ + -v "$PWD":/app -w /app \ + php:7.4-cli php bin/generate-golden.php diff --git a/bin/generate-golden.php b/bin/generate-golden.php new file mode 100644 index 00000000..e940773c --- /dev/null +++ b/bin/generate-golden.php @@ -0,0 +1,50 @@ + $entry ) { + $json = \WP_Parser\Golden\to_json( \WP_Parser\Golden\parse_entry( $entry ) ); + file_put_contents( \WP_Parser\Golden\snapshot_path( $slug ), $json ); + printf( " wrote %-32s %6d bytes\n", $slug, strlen( $json ) ); + $count++; +} + +printf( "\nDone. %d snapshot(s) written to %s\n", $count, $snapshots ); +printf( "PHP %s | php-parser/reflection as installed in vendor/\n", PHP_VERSION ); diff --git a/composer.json b/composer.json index aa09726d..5527b571 100644 --- a/composer.json +++ b/composer.json @@ -20,18 +20,21 @@ "issues": "https://github.com/WordPress/phpdoc-parser/issues" }, "require" : { - "php" : ">=5.4", - "composer/installers" : "~1.0", - "phpdocumentor/reflection" : "~3.0", + "php" : ">=8.2", + "composer/installers" : "^1.0 || ^2.0", + "nikic/php-parser" : "^5.0", + "phpstan/phpdoc-parser" : "^2.0", + "phpdocumentor/reflection-docblock": "^6.0", + "phpdocumentor/type-resolver" : "^2.0", "erusev/parsedown" : "~1.7", "scribu/lib-posts-to-posts": "dev-master@dev", "scribu/scb-framework" : "dev-master@dev", "psr/log" : "~1.0" }, "require-dev" : { - "phpunit/phpunit": "^7", + "phpunit/phpunit": "^9", "spatie/phpunit-watcher": "^1.23", - "yoast/phpunit-polyfills": "^1.0" + "yoast/phpunit-polyfills": "^2.0" }, "scripts" : { "test": "phpunit", @@ -43,6 +46,9 @@ "files" : ["lib/runner.php", "lib/template.php"] }, "config": { + "platform": { + "php": "8.2" + }, "allow-plugins": { "composer/installers": true }, diff --git a/composer.lock b/composer.lock index 55aea9d6..37b43abf 100644 --- a/composer.lock +++ b/composer.lock @@ -4,43 +4,41 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "96d200642f6aded313abd6eb270909ee", + "content-hash": "c33d9a0a445fbf3ecd891ef43c6e92c0", "packages": [ { "name": "composer/installers", - "version": "v1.12.0", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/composer/installers.git", - "reference": "d20a64ed3c94748397ff5973488761b22f6d3f19" + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/installers/zipball/d20a64ed3c94748397ff5973488761b22f6d3f19", - "reference": "d20a64ed3c94748397ff5973488761b22f6d3f19", + "url": "https://api.github.com/repos/composer/installers/zipball/12fb2dfe5e16183de69e784a7b84046c43d97e8e", + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0 || ^2.0" - }, - "replace": { - "roundcube/plugin-installer": "*", - "shama/baton": "*" + "composer-plugin-api": "^1.0 || ^2.0", + "php": "^7.2 || ^8.0" }, "require-dev": { - "composer/composer": "1.6.* || ^2.0", - "composer/semver": "^1 || ^3", - "phpstan/phpstan": "^0.12.55", - "phpstan/phpstan-phpunit": "^0.12.16", - "symfony/phpunit-bridge": "^4.2 || ^5", - "symfony/process": "^2.3" + "composer/composer": "^1.10.27 || ^2.7", + "composer/semver": "^1.7.2 || ^3.4.0", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-phpunit": "^1", + "symfony/phpunit-bridge": "^7.1.1", + "symfony/process": "^5 || ^6 || ^7" }, "type": "composer-plugin", "extra": { "class": "Composer\\Installers\\Plugin", "branch-alias": { - "dev-main": "1.x-dev" - } + "dev-main": "2.x-dev" + }, + "plugin-modifies-install-path": true }, "autoload": { "psr-4": { @@ -61,7 +59,6 @@ "description": "A multi-framework Composer library installer", "homepage": "https://composer.github.io/installers/", "keywords": [ - "Craft", "Dolibarr", "Eliasis", "Hurad", @@ -82,7 +79,6 @@ "Whmcs", "WolfCMS", "agl", - "aimeos", "annotatecms", "attogram", "bitrix", @@ -91,6 +87,7 @@ "cockpit", "codeigniter", "concrete5", + "concreteCMS", "croogo", "dokuwiki", "drupal", @@ -101,7 +98,6 @@ "grav", "installer", "itop", - "joomla", "known", "kohana", "laravel", @@ -110,6 +106,7 @@ "magento", "majima", "mako", + "matomo", "mediawiki", "miaoxing", "modulework", @@ -129,9 +126,7 @@ "silverstripe", "sydes", "sylius", - "symfony", "tastyigniter", - "typo3", "wordpress", "yawik", "zend", @@ -139,7 +134,7 @@ ], "support": { "issues": "https://github.com/composer/installers/issues", - "source": "https://github.com/composer/installers/tree/v1.12.0" + "source": "https://github.com/composer/installers/tree/v2.3.0" }, "funding": [ { @@ -155,28 +150,76 @@ "type": "tidelift" } ], - "time": "2021-09-13T08:19:44+00:00" + "time": "2024-06-24T20:46:46+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.6", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=14" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" + }, + "time": "2026-02-07T07:09:04+00:00" }, { "name": "erusev/parsedown", - "version": "1.7.4", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/erusev/parsedown.git", - "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3" + "reference": "96baaad00f71ba04d76e45b4620f54d3beabd6f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3", - "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/96baaad00f71ba04d76e45b4620f54d3beabd6f7", + "reference": "96baaad00f71ba04d76e45b4620f54d3beabd6f7", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=5.3.0" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35" + "phpunit/phpunit": "^7.5|^8.5|^9.6" }, "type": "library", "autoload": { @@ -203,38 +246,53 @@ ], "support": { "issues": "https://github.com/erusev/parsedown/issues", - "source": "https://github.com/erusev/parsedown/tree/1.7.x" + "source": "https://github.com/erusev/parsedown/tree/1.8.0" }, - "time": "2019-12-30T22:54:17+00:00" + "funding": [ + { + "url": "https://github.com/erusev", + "type": "github" + } + ], + "time": "2026-02-16T11:41:01+00:00" }, { "name": "nikic/php-parser", - "version": "v1.4.1", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", - "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=5.3" + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" }, + "bin": [ + "bin/php-parse" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "5.x-dev" } }, "autoload": { - "files": [ - "lib/bootstrap.php" - ] + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -252,103 +310,105 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/1.x" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2015-09-19T14:15:08+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { - "name": "phpdocumentor/reflection", - "version": "3.0.1", + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/Reflection.git", - "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d" + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", - "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "nikic/php-parser": "^1.0", - "php": ">=5.3.3", - "phpdocumentor/reflection-docblock": "~2.0", - "psr/log": "~1.0" - }, - "require-dev": { - "behat/behat": "~2.4", - "mockery/mockery": "~0.8", - "phpunit/phpunit": "~4.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/", - "tests/unit/", - "tests/mocks/" - ] + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Reflection library to do Static Analysis for PHP Projects", + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", "homepage": "http://www.phpdoc.org", "keywords": [ + "FQSEN", "phpDocumentor", "phpdoc", "reflection", "static analysis" ], "support": { - "issues": "https://github.com/phpDocumentor/Reflection/issues", - "source": "https://github.com/phpDocumentor/Reflection/tree/master" + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" }, - "time": "2016-05-21T08:42:32+00:00" + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "2.0.5", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b" + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e6a969a640b00d8daa3c66518b0405fb41ae0c4b", - "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/7bae67520aa9f5ecc506d646810bd40d9da54582", + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582", "shasum": "" }, "require": { - "php": ">=5.3.3" + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^2.0", + "phpstan/phpdoc-parser": "^2.0", + "webmozart/assert": "^1.9.1 || ^2" }, "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "dflydev/markdown": "~1.0", - "erusev/parsedown": "~1.0" + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26", + "shipmonk/dead-code-detector": "^0.5.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "5.x-dev" } }, "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/" - ] + "psr-4": { + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -358,14 +418,124 @@ "authors": [ { "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" } ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/2.x" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.3" + }, + "time": "2026-03-18T20:49:53+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" + }, + "time": "2026-01-06T21:53:42+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" }, - "time": "2016-01-25T08:17:30+00:00" + "time": "2026-01-25T14:56:51+00:00" }, { "name": "psr/log", @@ -498,6 +668,72 @@ "wiki": "https://github.com/scribu/wp-scb-framework/wiki" }, "time": "2020-03-15T20:51:58+00:00" + }, + { + "name": "webmozart/assert", + "version": "2.4.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "9007ea6f45ecf352a9422b36644e4bfc039b9155" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9007ea6f45ecf352a9422b36644e4bfc039b9155", + "reference": "9007ea6f45ecf352a9422b36644e4bfc039b9155", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, + "branch-alias": { + "dev-master": "2.0-dev", + "dev-feature/2-0": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.4.0" + }, + "time": "2026-05-20T13:07:01+00:00" } ], "packages-dev": [ @@ -579,25 +815,25 @@ }, { "name": "clue/term-react", - "version": "v1.3.0", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/clue/reactphp-term.git", - "reference": "eb6eb063eda04a714ef89f066586a2c49588f7ca" + "reference": "00f297dc597eaee2ebf98af8f27cca5d21d60fa3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-term/zipball/eb6eb063eda04a714ef89f066586a2c49588f7ca", - "reference": "eb6eb063eda04a714ef89f066586a2c49588f7ca", + "url": "https://api.github.com/repos/clue/reactphp-term/zipball/00f297dc597eaee2ebf98af8f27cca5d21d60fa3", + "reference": "00f297dc597eaee2ebf98af8f27cca5d21d60fa3", "shasum": "" }, "require": { "php": ">=5.3", - "react/stream": "^1.0 || ^0.7" + "react/stream": "^1.2" }, "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/event-loop": "^1.2" }, "type": "library", "autoload": { @@ -636,7 +872,7 @@ ], "support": { "issues": "https://github.com/clue/reactphp-term/issues", - "source": "https://github.com/clue/reactphp-term/tree/v1.3.0" + "source": "https://github.com/clue/reactphp-term/tree/v1.4.0" }, "funding": [ { @@ -648,20 +884,20 @@ "type": "github" } ], - "time": "2020-11-06T11:50:12+00:00" + "time": "2024-01-30T10:22:09+00:00" }, { "name": "clue/utf8-react", - "version": "v1.2.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/clue/reactphp-utf8.git", - "reference": "8bc3f8c874cdf642c8f10f9ae93aadb8cd63da96" + "reference": "d5cd04d39cb5457aa5df830b7c4b301d2694217e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-utf8/zipball/8bc3f8c874cdf642c8f10f9ae93aadb8cd63da96", - "reference": "8bc3f8c874cdf642c8f10f9ae93aadb8cd63da96", + "url": "https://api.github.com/repos/clue/reactphp-utf8/zipball/d5cd04d39cb5457aa5df830b7c4b301d2694217e", + "reference": "d5cd04d39cb5457aa5df830b7c4b301d2694217e", "shasum": "" }, "require": { @@ -669,7 +905,7 @@ "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4 || ^0.3" }, "require-dev": { - "phpunit/phpunit": "^9.3 ||^5.7 || ^4.8", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", "react/stream": "^1.0 || ^0.7" }, "type": "library", @@ -699,7 +935,7 @@ ], "support": { "issues": "https://github.com/clue/reactphp-utf8/issues", - "source": "https://github.com/clue/reactphp-utf8/tree/v1.2.0" + "source": "https://github.com/clue/reactphp-utf8/tree/v1.3.0" }, "funding": [ { @@ -711,34 +947,34 @@ "type": "github" } ], - "time": "2020-11-06T11:48:09+00:00" + "time": "2023-12-06T14:52:17+00:00" }, { "name": "doctrine/instantiator", - "version": "1.4.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9", + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.22" + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" }, "type": "library", "autoload": { @@ -765,7 +1001,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { @@ -781,32 +1017,32 @@ "type": "tidelift" } ], - "time": "2022-03-03T08:28:38+00:00" + "time": "2022-12-30T00:23:10+00:00" }, { "name": "evenement/evenement", - "version": "v3.0.1", + "version": "v3.0.2", "source": { "type": "git", "url": "https://github.com/igorw/evenement.git", - "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", - "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", "shasum": "" }, "require": { "php": ">=7.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9 || ^6" }, "type": "library", "autoload": { - "psr-0": { - "Evenement": "src" + "psr-4": { + "Evenement\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -826,37 +1062,48 @@ ], "support": { "issues": "https://github.com/igorw/evenement/issues", - "source": "https://github.com/igorw/evenement/tree/master" + "source": "https://github.com/igorw/evenement/tree/v3.0.2" }, - "time": "2017-07-23T21:35:13+00:00" + "time": "2023-08-08T05:53:35+00:00" }, { "name": "jolicode/jolinotif", - "version": "v2.4.0", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/jolicode/JoliNotif.git", - "reference": "a15bfc0d5aef432f150385924ede4e099643edb7" + "reference": "6f6490f8fd062deda601a8bfc03ea03305253d29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jolicode/JoliNotif/zipball/a15bfc0d5aef432f150385924ede4e099643edb7", - "reference": "a15bfc0d5aef432f150385924ede4e099643edb7", + "url": "https://api.github.com/repos/jolicode/JoliNotif/zipball/6f6490f8fd062deda601a8bfc03ea03305253d29", + "reference": "6f6490f8fd062deda601a8bfc03ea03305253d29", "shasum": "" }, "require": { - "php": ">=7.4", - "symfony/process": "^4.0|^5.0|^6.0" + "jolicode/php-os-helper": "^0.3", + "php": ">=8.2", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/deprecation-contracts": "^3", + "symfony/process": "^6.4 || ^7.3 || ^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0", - "symfony/finder": "^5.0", - "symfony/phpunit-bridge": "^5.0" + "monolog/monolog": "^3.9", + "symfony/finder": "^6.4 || ^7.3 || ^8.0", + "symfony/phpunit-bridge": "^7.3 || ^8.0" + }, + "suggest": { + "ext-ffi": "Needed to send notifications via libnotify on Linux" }, "bin": [ "jolinotif" ], "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, "autoload": { "psr-4": { "Joli\\JoliNotif\\": "src/" @@ -882,7 +1129,7 @@ ], "support": { "issues": "https://github.com/jolicode/JoliNotif/issues", - "source": "https://github.com/jolicode/JoliNotif/tree/v2.4.0" + "source": "https://github.com/jolicode/JoliNotif/tree/v3.2.0" }, "funding": [ { @@ -890,20 +1137,70 @@ "type": "tidelift" } ], - "time": "2021-12-01T16:20:42+00:00" + "time": "2025-10-16T21:31:31+00:00" + }, + { + "name": "jolicode/php-os-helper", + "version": "v0.3.0", + "source": { + "type": "git", + "url": "https://github.com/jolicode/php-os-helper.git", + "reference": "e71596ae67d477fe214a5908f5522b9e4d19fd37" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jolicode/php-os-helper/zipball/e71596ae67d477fe214a5908f5522b9e4d19fd37", + "reference": "e71596ae67d477fe214a5908f5522b9e4d19fd37", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "JoliCode\\PhpOsHelper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Loïck Piera", + "email": "pyrech@gmail.com" + } + ], + "description": "Helpers to detect the OS of the machine where PHP is running.", + "keywords": [ + "linux", + "os", + "osx", + "php", + "windows" + ], + "support": { + "issues": "https://github.com/jolicode/php-os-helper/issues", + "source": "https://github.com/jolicode/php-os-helper/tree/v0.3.0" + }, + "time": "2025-10-11T13:02:06+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.11.0", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -911,11 +1208,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -941,7 +1239,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -949,32 +1247,34 @@ "type": "tidelift" } ], - "time": "2022-03-03T13:19:32+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "phar-io/manifest", - "version": "1.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -1006,26 +1306,32 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/master" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2018-07-08T19:23:20+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", - "version": "2.0.1", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -1057,168 +1363,112 @@ "description": "Library for handling version information and constraints", "support": { "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/master" + "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "time": "2018-07-08T19:19:57+00:00" + "time": "2022-02-21T01:04:05+00:00" }, { - "name": "phpspec/prophecy", - "version": "v1.10.3", + "name": "phpunit/php-code-coverage", + "version": "9.2.32", "source": { "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + "phpunit/phpunit": "^9.6" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.10.x-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" + "coverage", + "testing", + "xunit" ], "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" }, - "time": "2020-03-05T15:02:03+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "6.1.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^7.1", - "phpunit/php-file-iterator": "^2.0", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.0", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.1 || ^4.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "suggest": { - "ext-xdebug": "^2.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ + "funding": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/master" - }, - "time": "2018-10-31T16:06:48+00:00" + "time": "2024-08-22T04:23:01+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "2.0.5", + "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5" + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", - "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1245,7 +1495,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.5" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" }, "funding": [ { @@ -1253,26 +1503,38 @@ "type": "github" } ], - "time": "2021-12-02T12:42:26+00:00" + "time": "2021-12-02T12:48:52+00:00" }, { - "name": "phpunit/php-text-template", - "version": "1.2.1", + "name": "phpunit/php-invoker", + "version": "3.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -1289,41 +1551,47 @@ "role": "lead" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ - "template" + "process" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" }, - "time": "2015-06-21T13:50:34+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" }, { - "name": "phpunit/php-timer", - "version": "2.1.3", + "name": "phpunit/php-text-template", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1342,14 +1610,14 @@ "role": "lead" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "timer" + "template" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" }, "funding": [ { @@ -1357,33 +1625,32 @@ "type": "github" } ], - "time": "2020-11-30T08:20:02+00:00" + "time": "2020-10-26T05:33:50+00:00" }, { - "name": "phpunit/php-token-stream", - "version": "3.1.3", + "name": "phpunit/php-timer", + "version": "5.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "9c1da83261628cb24b6a6df371b6e312b3954768" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9c1da83261628cb24b6a6df371b6e312b3954768", - "reference": "9c1da83261628cb24b6a6df371b6e312b3954768", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -1398,17 +1665,18 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "tokenizer" + "timer" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1.3" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" }, "funding": [ { @@ -1416,58 +1684,54 @@ "type": "github" } ], - "abandoned": true, - "time": "2021-07-26T12:15:06+00:00" + "time": "2020-10-26T13:16:10+00:00" }, { "name": "phpunit/phpunit", - "version": "7.5.20", + "version": "9.6.34", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9467db479d1b0487c99733bb1e7944d32deded2c" + "reference": "b36f02317466907a230d3aa1d34467041271ef4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c", - "reference": "9467db479d1b0487c99733bb1e7944d32deded2c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b36f02317466907a230d3aa1d34467041271ef4a", + "reference": "b36f02317466907a230d3aa1d34467041271ef4a", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.1", + "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "^1.7", - "phar-io/manifest": "^1.0.2", - "phar-io/version": "^2.0", - "php": "^7.1", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^6.0.7", - "phpunit/php-file-iterator": "^2.0.1", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0", - "sebastian/environment": "^4.0", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpunit/phpunit-mock-objects": "*" - }, - "require-dev": { - "ext-pdo": "*" + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.32", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", + "sebastian/comparator": "^4.0.10", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.8", + "sebastian/global-state": "^5.0.8", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", + "sebastian/version": "^3.0.2" }, "suggest": { - "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "bin": [ "phpunit" @@ -1475,10 +1739,13 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.5-dev" + "dev-master": "9.6-dev" } }, "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], "classmap": [ "src/" ] @@ -1503,28 +1770,56 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/7.5.20" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.34" }, - "time": "2020-01-08T08:45:45+00:00" + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2026-01-27T05:45:00+00:00" }, { "name": "psr/container", - "version": "1.1.2", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { "php": ">=7.4.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -1551,39 +1846,37 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2021-11-05T16:50:12+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { "name": "react/event-loop", - "version": "v1.3.0", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/reactphp/event-loop.git", - "reference": "187fb56f46d424afb6ec4ad089269c72eec2e137" + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/187fb56f46d424afb6ec4ad089269c72eec2e137", - "reference": "187fb56f46d424afb6ec4ad089269c72eec2e137", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", "shasum": "" }, "require": { "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "suggest": { - "ext-event": "~1.0 for ExtEventLoop", - "ext-pcntl": "For signal handling support when using the StreamSelectLoop", - "ext-uv": "* for ExtUvLoop" + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" }, "type": "library", "autoload": { "psr-4": { - "React\\EventLoop\\": "src" + "React\\EventLoop\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1619,32 +1912,28 @@ ], "support": { "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.3.0" + "source": "https://github.com/reactphp/event-loop/tree/v1.6.0" }, "funding": [ { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2022-03-17T11:10:22+00:00" + "time": "2025-11-17T20:46:25+00:00" }, { "name": "react/stream", - "version": "v1.2.0", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/reactphp/stream.git", - "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9" + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/7a423506ee1903e89f1e08ec5f0ed430ff784ae9", - "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", "shasum": "" }, "require": { @@ -1654,12 +1943,12 @@ }, "require-dev": { "clue/stream-filter": "~1.2", - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "type": "library", "autoload": { "psr-4": { - "React\\Stream\\": "src" + "React\\Stream\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1701,44 +1990,152 @@ ], "support": { "issues": "https://github.com/reactphp/stream/issues", - "source": "https://github.com/reactphp/stream/tree/v1.2.0" + "source": "https://github.com/reactphp/stream/tree/v1.4.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-11T12:45:25+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" }, "funding": [ { - "url": "https://github.com/WyriHaximus", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, + } + ], + "time": "2024-03-02T06:27:43+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ { - "url": "https://github.com/clue", + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2021-07-11T12:37:55+00:00" + "time": "2020-10-26T13:08:54+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1760,7 +2157,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" }, "funding": [ { @@ -1768,34 +2165,34 @@ "type": "github" } ], - "time": "2020-11-30T08:15:22+00:00" + "time": "2020-09-28T05:30:19+00:00" }, { "name": "sebastian/comparator", - "version": "3.0.3", + "version": "4.0.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d", + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d", "shasum": "" }, "require": { - "php": ">=7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1834,7 +2231,76 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.10" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-01-24T09:22:56+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" }, "funding": [ { @@ -1842,33 +2308,33 @@ "type": "github" } ], - "time": "2020-11-30T08:04:30+00:00" + "time": "2023-12-22T06:19:30+00:00" }, { "name": "sebastian/diff", - "version": "3.0.3", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1900,7 +2366,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" }, "funding": [ { @@ -1908,27 +2374,27 @@ "type": "github" } ], - "time": "2020-11-30T07:59:04+00:00" + "time": "2024-03-02T06:30:58+00:00" }, { "name": "sebastian/environment", - "version": "4.2.4", + "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^7.5" + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-posix": "*" @@ -1936,7 +2402,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -1963,7 +2429,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { @@ -1971,34 +2437,34 @@ "type": "github" } ], - "time": "2020-11-30T07:53:42+00:00" + "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.4", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db" + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", - "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", "shasum": "" }, "require": { - "php": ">=7.0", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2033,42 +2499,57 @@ } ], "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2021-11-11T13:51:24+00:00" + "time": "2025-09-24T06:03:27+00:00" }, { "name": "sebastian/global-state", - "version": "2.0.0", + "version": "5.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "ext-dom": "*", + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" @@ -2076,7 +2557,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -2101,36 +2582,111 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/2.0.0" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" }, - "time": "2017-04-27T15:39:26+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" + } + ], + "time": "2025-08-10T07:10:35+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" }, { "name": "sebastian/object-enumerator", - "version": "3.0.4", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { - "php": ">=7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2152,7 +2708,7 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" }, "funding": [ { @@ -2160,32 +2716,32 @@ "type": "github" } ], - "time": "2020-11-30T07:40:27+00:00" + "time": "2020-10-26T13:12:34+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.2", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -2207,7 +2763,7 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" }, "funding": [ { @@ -2215,32 +2771,32 @@ "type": "github" } ], - "time": "2020-11-30T07:37:18+00:00" + "time": "2020-10-26T13:14:26+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.1", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2267,40 +2823,109 @@ } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2020-11-30T07:34:24+00:00" + "time": "2025-08-10T06:57:39+00:00" }, { "name": "sebastian/resource-operations", - "version": "2.0.2", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-14T16:00:52+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2315,14 +2940,15 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { @@ -2330,29 +2956,29 @@ "type": "github" } ], - "time": "2020-11-30T07:30:19+00:00" + "time": "2023-02-03T06:13:03+00:00" }, { "name": "sebastian/version", - "version": "2.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -2375,40 +3001,45 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" }, - "time": "2016-10-03T07:35:21+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" }, { "name": "spatie/phpunit-watcher", - "version": "1.23.6", + "version": "1.24.4", "source": { "type": "git", "url": "https://github.com/spatie/phpunit-watcher.git", - "reference": "c192fff763810c8378511bcf0069df4b91478866" + "reference": "3b0e68596c70eba9bec99566b5e2199afb9f289d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/phpunit-watcher/zipball/c192fff763810c8378511bcf0069df4b91478866", - "reference": "c192fff763810c8378511bcf0069df4b91478866", + "url": "https://api.github.com/repos/spatie/phpunit-watcher/zipball/3b0e68596c70eba9bec99566b5e2199afb9f289d", + "reference": "3b0e68596c70eba9bec99566b5e2199afb9f289d", "shasum": "" }, "require": { - "clue/stdio-react": "^2.4", - "jolicode/jolinotif": "^2.2", - "php": "^7.2 | ^8.0 | ^8.1", - "symfony/console": "^5 | ^6", - "symfony/finder": "^5.4 | ^6", - "symfony/process": "^5.4 | ^6", - "symfony/yaml": "^5.2 | ^6", - "yosymfony/resource-watcher": "^2.0 | ^3.0" + "clue/stdio-react": "^2.6", + "jolicode/jolinotif": "^2.7.1 || ^3.0", + "php": "^8.1", + "symfony/console": "^6.0 || ^7.0.7 || ^8.0", + "symfony/finder": "^6.0 || ^7.0.7 || ^8.0", + "symfony/process": "^6.0 || ^7.0.7 || ^8.0", + "symfony/yaml": "^6.0 || ^7.0.7 || ^8.0" }, "conflict": { - "symfony/console": "<5.2", - "yosymfony/resource-watcher": "<2.0" + "symfony/console": "<5.2" }, "require-dev": { - "phpunit/phpunit": "^8.6 | ^9.0" + "phpunit/phpunit": "^10.5.20 | ^11.1.3 || ^12.0", + "symfony/filesystem": "^6.0 || ^7.0.7 || ^8.0" }, "bin": [ "phpunit-watcher" @@ -2439,58 +3070,59 @@ ], "support": { "issues": "https://github.com/spatie/phpunit-watcher/issues", - "source": "https://github.com/spatie/phpunit-watcher/tree/1.23.6" + "source": "https://github.com/spatie/phpunit-watcher/tree/1.24.4" }, - "time": "2022-01-31T11:57:13+00:00" + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2026-01-05T09:33:23+00:00" }, { "name": "symfony/console", - "version": "v5.4.12", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c072aa8f724c3af64e2c7a96b796a4863d24dba1" + "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c072aa8f724c3af64e2c7a96b796a4863d24dba1", - "reference": "c072aa8f724c3af64e2c7a96b796a4863d24dba1", + "url": "https://api.github.com/repos/symfony/console/zipball/85095d2573eaefaf35e40b9513a9bf09f72cd217", + "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.1|^6.0" + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2|^8.0" }, "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { - "psr/log-implementation": "1.0|2.0" + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -2519,12 +3151,12 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.12" + "source": "https://github.com/symfony/console/tree/v7.4.13" }, "funding": [ { @@ -2535,38 +3167,42 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-08-17T13:18:05+00:00" + "time": "2026-05-24T08:56:14+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.2", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { @@ -2591,7 +3227,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" }, "funding": [ { @@ -2602,31 +3238,36 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2026-04-13T15:52:40+00:00" }, { "name": "symfony/finder", - "version": "v5.4.11", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c" + "reference": "e0be088d22278583a82da281886e8c3592fbf149" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/7872a66f57caffa2916a584db1aa7f12adc76f8c", - "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c", + "url": "https://api.github.com/repos/symfony/finder/zipball/e0be088d22278583a82da281886e8c3592fbf149", + "reference": "e0be088d22278583a82da281886e8c3592fbf149", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -2654,7 +3295,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.11" + "source": "https://github.com/symfony/finder/tree/v7.4.8" }, "funding": [ { @@ -2665,29 +3306,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-07-29T07:37:50+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.26.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -2697,12 +3342,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -2736,7 +3378,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { @@ -2747,41 +3389,42 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.26.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "433d05519ce6990bf3530fba6957499d327395c2" + "reference": "e9247d281d694a5120554d9afaf54e070e88a603" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", - "reference": "433d05519ce6990bf3530fba6957499d327395c2", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", + "reference": "e9247d281d694a5120554d9afaf54e070e88a603", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -2817,7 +3460,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" }, "funding": [ { @@ -2828,41 +3471,42 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2026-05-26T05:58:03+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.26.0", + "version": "v1.38.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd" + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -2901,7 +3545,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" }, "funding": [ { @@ -2912,29 +3556,34 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2026-05-25T13:48:31+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.26.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/14c5439eec4ccff081ac14eca2dc57feb2a66d92", + "reference": "14c5439eec4ccff081ac14eca2dc57feb2a66d92", "shasum": "" }, "require": { - "php": ">=7.1" + "ext-iconv": "*", + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -2944,12 +3593,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -2984,86 +3630,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85", - "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.1" }, "funding": [ { @@ -3075,86 +3642,7 @@ "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-24T11:49:31+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.26-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/nicolas-grekas", "type": "github" }, { @@ -3162,25 +3650,24 @@ "type": "tidelift" } ], - "time": "2022-05-10T07:21:04+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { "name": "symfony/process", - "version": "v5.4.11", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1" + "reference": "f5804be144caceb570f6747519999636b664f24c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/6e75fe6874cbc7e4773d049616ab450eff537bf1", - "reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1", + "url": "https://api.github.com/repos/symfony/process/zipball/f5804be144caceb570f6747519999636b664f24c", + "reference": "f5804be144caceb570f6747519999636b664f24c", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.2" }, "type": "library", "autoload": { @@ -3208,7 +3695,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.11" + "source": "https://github.com/symfony/process/tree/v7.4.13" }, "funding": [ { @@ -3219,52 +3706,56 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-06-27T16:58:25+00:00" + "time": "2026-05-23T16:05:06+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.5.2", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3291,7 +3782,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" }, "funding": [ { @@ -3302,43 +3793,48 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-05-30T19:17:29+00:00" + "time": "2026-03-28T09:44:51+00:00" }, { "name": "symfony/string", - "version": "v5.4.12", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "2fc515e512d721bf31ea76bd02fe23ada4640058" + "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/2fc515e512d721bf31ea76bd02fe23ada4640058", - "reference": "2fc515e512d721bf31ea76bd02fe23ada4640058", + "url": "https://api.github.com/repos/symfony/string/zipball/961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", + "reference": "961683010db3b27ec6ebcd7308e6e1ee8fa7ffde", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-grapheme": "~1.33", "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": ">=3.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0|^6.0" + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3377,7 +3873,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.12" + "source": "https://github.com/symfony/string/tree/v7.4.13" }, "funding": [ { @@ -3388,40 +3884,41 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-08-12T17:03:11+00:00" + "time": "2026-05-23T15:23:29+00:00" }, { "name": "symfony/yaml", - "version": "v5.4.12", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "7a3aa21ac8ab1a96cc6de5bbcab4bc9fc943b18c" + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/7a3aa21ac8ab1a96cc6de5bbcab4bc9fc943b18c", - "reference": "7a3aa21ac8ab1a96cc6de5bbcab4bc9fc943b18c", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a7ec3b1156faf8815db7683ec7c1e7338e6f977c", + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<5.3" + "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "^5.3|^6.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "symfony/console": "^6.4|^7.0|^8.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -3452,7 +3949,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.12" + "source": "https://github.com/symfony/yaml/tree/v7.4.13" }, "funding": [ { @@ -3463,25 +3960,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-08-02T15:52:22+00:00" + "time": "2026-05-25T06:06:12+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -3510,7 +4011,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -3518,34 +4019,35 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2025-11-17T20:03:58+00:00" }, { "name": "yoast/phpunit-polyfills", - "version": "1.0.3", + "version": "2.0.5", "source": { "type": "git", "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "5ea3536428944955f969bc764bbe09738e151ada" + "reference": "1a6aecc9ebe4a9cea4e1047d0e6c496e52314c27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/5ea3536428944955f969bc764bbe09738e151ada", - "reference": "5ea3536428944955f969bc764bbe09738e151ada", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/1a6aecc9ebe4a9cea4e1047d0e6c496e52314c27", + "reference": "1a6aecc9ebe4a9cea4e1047d0e6c496e52314c27", "shasum": "" }, "require": { - "php": ">=5.4", - "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + "php": ">=5.6", + "phpunit/phpunit": "^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0" }, "require-dev": { - "yoast/yoastcs": "^2.2.0" + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "yoast/yoastcs": "^3.2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.x-dev", - "dev-develop": "1.x-dev" + "dev-main": "4.x-dev" } }, "autoload": { @@ -3577,66 +4079,10 @@ ], "support": { "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", + "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", "source": "https://github.com/Yoast/PHPUnit-Polyfills" }, - "time": "2021-11-23T01:37:03+00:00" - }, - { - "name": "yosymfony/resource-watcher", - "version": "v3.0.0", - "source": { - "type": "git", - "url": "https://github.com/yosymfony/resource-watcher.git", - "reference": "2f197cee0231c06db865d4ad2d8d7cd3faead2f8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/yosymfony/resource-watcher/zipball/2f197cee0231c06db865d4ad2d8d7cd3faead2f8", - "reference": "2f197cee0231c06db865d4ad2d8d7cd3faead2f8", - "shasum": "" - }, - "require": { - "php": ">=5.6", - "symfony/finder": "^2.7|^3.0|^4.0|^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7", - "symfony/filesystem": "^2.7|^3.0|^4.0|^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "Yosymfony\\ResourceWatcher\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Victor Puertas", - "email": "vpgugr@gmail.com" - } - ], - "description": "A simple resource watcher using Symfony Finder", - "homepage": "http://yosymfony.com", - "keywords": [ - "finder", - "resources", - "symfony", - "watcher" - ], - "support": { - "issues": "https://github.com/yosymfony/resource-watcher/issues", - "source": "https://github.com/yosymfony/resource-watcher/tree/master" - }, - "time": "2020-06-10T14:58:36+00:00" + "time": "2025-08-10T05:13:49+00:00" } ], "aliases": [], @@ -3648,8 +4094,11 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.4" + "php": ">=8.2" + }, + "platform-dev": {}, + "platform-overrides": { + "php": "8.2" }, - "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.9.0" } diff --git a/lib/class-docblock-adapter.php b/lib/class-docblock-adapter.php new file mode 100644 index 00000000..eeac4591 --- /dev/null +++ b/lib/class-docblock-adapter.php @@ -0,0 +1,404 @@ +docblock = $docblock; + $this->context = $context; + } + + /** + * Build an adapter from a node's doc comment, or null when there is none. + * + * @param \PhpParser\Node $node Node carrying the doc comment. + * @param string $namespace Namespace to resolve types against ('global' => root). + * @param array $aliases Import aliases (alias => FQN). + * + * @return Docblock_Adapter|null + */ + public static function from_node( $node, $namespace, array $aliases ) { + $comment = $node->getDocComment(); + if ( null === $comment ) { + return null; + } + + return self::from_text( $comment->getText(), $namespace, $aliases ); + } + + /** + * Build an adapter from raw docblock text. + * + * @param string $text + * @param string $namespace + * @param array $aliases + * + * @return Docblock_Adapter|null + */ + public static function from_text( $text, $namespace, array $aliases ) { + static $factory = null; + if ( null === $factory ) { + $factory = DocBlockFactory::createInstance(); + } + + $context = new Context( 'global' === $namespace ? '' : $namespace, $aliases ); + + try { + $docblock = $factory->create( $text, $context ); + } catch ( \Throwable $e ) { + return null; + } + + return new self( $docblock, $context ); + } + + public function getShortDescription() { + return $this->docblock->getSummary(); + } + + public function getLongDescription() { + return new Description_Adapter( $this->docblock->getDescription() ); + } + + /** + * @return object[] Tag adapters. + */ + public function getTags() { + $tags = array(); + foreach ( $this->docblock->getTags() as $tag ) { + $tags[] = $this->adapt_tag( $tag ); + } + + return $tags; + } + + /** + * Map a reflection-docblock tag to the adapter exposing the right legacy methods. + * + * @param Tags\BaseTag $tag + * + * @return object + */ + protected function adapt_tag( $tag ) { + $name = $tag->getName(); + + // Tags reflection-docblock can't parse strictly (e.g. @see with a non-FQSEN + // reference, or @param with a $this variable) come back as InvalidTag; + // reconstruct from the raw body to match the legacy loose parsing. + if ( $tag instanceof Tags\InvalidTag ) { + return $this->adapt_invalid_tag( $tag, $name ); + } + + $description = self::render_description( method_exists( $tag, 'getDescription' ) ? $tag->getDescription() : null ); + + // @param and @var: type + variable name. + if ( $tag instanceof Tags\Param || $tag instanceof Tags\Var_ ) { + $variable = $tag->getVariableName(); + + return new Param_Tag_Adapter( + $name, + $description, + self::type_to_legacy_strings( $tag->getType() ), + $variable ? '$' . $variable : '' + ); + } + + // @return and other typed tags: type, no variable. + if ( $tag instanceof Tags\TagWithType ) { + return new Typed_Tag_Adapter( $name, $description, self::type_to_legacy_strings( $tag->getType() ) ); + } + + // @since and @deprecated: version becomes the content. + if ( $tag instanceof Tags\Since || $tag instanceof Tags\Deprecated ) { + return new Version_Tag_Adapter( $name, $description, (string) $tag->getVersion() ); + } + + // @see: a reference. reflection-docblock normalizes the target to an FQSEN + // (always leading-backslash); the legacy parser kept it as written, so undo + // the single normalization backslash to match. + if ( $tag instanceof Tags\See ) { + $reference = preg_replace( '/^\\\\/', '', (string) $tag->getReference() ); + + return new See_Tag_Adapter( $name, $description, $reference ); + } + + // @link: a URL. When no description is given the URL itself is the content. + if ( $tag instanceof Tags\Link ) { + $link = $tag->getLink(); + + return new Link_Tag_Adapter( $name, '' !== $description ? $description : $link, $link ); + } + + return new Generic_Tag_Adapter( $name, $description ); + } + + /** + * Reconstruct a tag reflection-docblock flagged invalid, recovering its body + * from the rendered "@name body" string. + * + * @param Tags\InvalidTag $tag + * @param string $name + * + * @return object + */ + protected function adapt_invalid_tag( $tag, $name ) { + $body = preg_replace( '/^@' . preg_quote( $name, '/' ) . '\s*/', '', $tag->render() ); + + // @see / @uses: the first token is the reference, the remainder the description. + if ( 'see' === $name || 'uses' === $name ) { + $parts = preg_split( '/\s+/', $body, 2 ); + + return new See_Tag_Adapter( + $name, + isset( $parts[1] ) ? $parts[1] : '', + isset( $parts[0] ) ? $parts[0] : '' + ); + } + + // @param / @var with a variable reflection-docblock rejects (e.g. $this): + // " <$variable> ". + if ( 'param' === $name || 'var' === $name || 'property' === $name ) { + $parts = preg_split( '/\s+/', $body, 3 ); + + if ( isset( $parts[1] ) && 0 === strpos( $parts[1], '$' ) ) { + return new Param_Tag_Adapter( + $name, + isset( $parts[2] ) ? $parts[2] : '', + $this->resolve_type_string( $parts[0] ), + $parts[1] + ); + } + } + + return new Generic_Tag_Adapter( $name, $body ); + } + + /** + * Resolve a written type string to the legacy array of type strings, using the + * docblock context (so class names get the leading-backslash FQN form). + * + * @param string $type_string + * + * @return string[] + */ + protected function resolve_type_string( $type_string ) { + static $resolver = null; + if ( null === $resolver ) { + $resolver = new TypeResolver(); + } + + try { + return self::type_to_legacy_strings( $resolver->resolve( $type_string, $this->context ) ); + } catch ( \Throwable $e ) { + return array( $type_string ); + } + } + + /** + * Render a tag's description object to a string. + * + * @param mixed $description + * + * @return string + */ + protected static function render_description( $description ) { + return $description ? $description->render() : ''; + } + + /** + * Convert a type-resolver Type to the legacy array of type strings. + * + * Compound (union) types are split into their parts; everything else renders + * to a single string. Object types resolve to a leading-backslash FQN via the + * docblock Context (e.g. WP_Post => \WP_Post). + * + * @param Type|null $type + * + * @return string[] + */ + public static function type_to_legacy_strings( $type ) { + if ( null === $type ) { + return array(); + } + + if ( $type instanceof Compound ) { + $types = array(); + foreach ( $type as $part ) { + $types[] = (string) $part; + } + + return $types; + } + + return array( (string) $type ); + } +} + +/** + * Wraps a reflection-docblock Description, exposing the legacy getFormattedContents(). + */ +class Description_Adapter { + + /** @var DocBlock\Description */ + protected $description; + + public function __construct( $description ) { + $this->description = $description; + } + + /** + * The long description as formatted HTML. + * + * The legacy reflection-docblock ran the body through a Markdown block parser + * (wrapping paragraphs in

); export_docblock() then collapses the soft line + * breaks via fix_newlines(). Parsedown reproduces that output. + * + * @return string + */ + public function getFormattedContents() { + $text = $this->description->render(); + + if ( '' !== $text && class_exists( 'Parsedown' ) ) { + $text = \Parsedown::instance()->text( $text ); + } + + return $text; + } +} + +/** + * Base tag: name + description only (e.g. @access, @global, @package). + */ +class Generic_Tag_Adapter { + + protected $name; + protected $description; + + public function __construct( $name, $description ) { + $this->name = $name; + $this->description = $description; + } + + public function getName() { + return $this->name; + } + + public function getDescription() { + return $this->description; + } +} + +/** + * A tag carrying types but no variable (e.g. @return). + */ +class Typed_Tag_Adapter extends Generic_Tag_Adapter { + + protected $types; + + public function __construct( $name, $description, array $types ) { + parent::__construct( $name, $description ); + $this->types = $types; + } + + public function getTypes() { + return $this->types; + } +} + +/** + * A tag carrying types and a variable name (e.g. @param, @var). + */ +class Param_Tag_Adapter extends Typed_Tag_Adapter { + + protected $variable; + + public function __construct( $name, $description, array $types, $variable ) { + parent::__construct( $name, $description, $types ); + $this->variable = $variable; + } + + public function getVariableName() { + return $this->variable; + } +} + +/** + * A versioned tag (e.g. @since, @deprecated). + */ +class Version_Tag_Adapter extends Generic_Tag_Adapter { + + protected $version; + + public function __construct( $name, $description, $version ) { + parent::__construct( $name, $description ); + $this->version = $version; + } + + public function getVersion() { + return $this->version; + } +} + +/** + * A reference tag (e.g. @see). + */ +class See_Tag_Adapter extends Generic_Tag_Adapter { + + protected $reference; + + public function __construct( $name, $description, $reference ) { + parent::__construct( $name, $description ); + $this->reference = $reference; + } + + public function getReference() { + return $this->reference; + } +} + +/** + * A link tag (e.g. @link). + */ +class Link_Tag_Adapter extends Generic_Tag_Adapter { + + protected $link; + + public function __construct( $name, $description, $link ) { + parent::__construct( $name, $description ); + $this->link = $link; + } + + public function getLink() { + return $this->link; + } +} diff --git a/lib/class-file-reflector.php b/lib/class-file-reflector.php index 62f401d7..f5023146 100644 --- a/lib/class-file-reflector.php +++ b/lib/class-file-reflector.php @@ -2,208 +2,325 @@ namespace WP_Parser; -use phpDocumentor\Reflection; -use phpDocumentor\Reflection\FileReflector; +use PhpParser\Comment\Doc; +use PhpParser\Node; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitor\NameResolver; +use PhpParser\NodeVisitorAbstract; +use PhpParser\ParserFactory; /** - * Reflection class for a full file. + * Reflects a single PHP file using nikic/php-parser 5. * - * Extends the FileReflector from phpDocumentor to parse out WordPress - * hooks and note function relationships. + * Replaces the former phpDocumentor FileReflector subclass. It walks the AST as + * a NodeVisitor, collecting the structural elements (functions, classes, their + * methods and properties) that runner.php exports, the WordPress hooks declared + * via do_action()/apply_filters(), and the functions/methods each element uses — + * all while keeping the exported array shape identical. */ -class File_Reflector extends FileReflector { +class File_Reflector extends NodeVisitorAbstract { + /** - * List of elements used in global scope in this file, indexed by element type. + * Elements used in file scope, indexed by element type (functions, methods, hooks). * - * @var array { - * @type Hook_Reflector[] $hooks The action and filters. - * @type Function_Call_Reflector[] $functions The functions called. - * } + * @var array */ public $uses = array(); + protected $filename; + protected $contents; + protected $path; + + /** @var Function_Reflector[] */ + protected $functions = array(); + + /** @var Class_Reflector[] */ + protected $classes = array(); + + protected $constants = array(); + protected $includes = array(); + + /** @var Docblock_Adapter|null The file-level docblock, if any. */ + protected $file_docblock = null; + + /** @var Node[] Stack of scope nodes (function/method/class) currently open. */ + protected $location = array(); + + /** @var Doc|null Last docblock seen on a non-documentable node, for the next hook. */ + protected $last_doc = null; + + /** @var string Current namespace ('global' at file scope). */ + protected $namespace = 'global'; + + /** @var array Map of import alias => fully qualified name for the current namespace. */ + protected $aliases = array(); + /** - * List of elements used in the current class scope, indexed by method. - * - * @var array[][] {@see \WP_Parser\File_Reflector::$uses} + * @param string $filename Absolute path to the file to reflect. */ - protected $method_uses_queue = array(); + public function __construct( $filename ) { + $this->filename = $filename; + $this->contents = file_get_contents( $filename ); + } /** - * Stack of classes/methods/functions currently being parsed. + * Set the (root-relative) path reported for this file. * - * @see \WP_Parser\FileReflector::getLocation() - * @var \phpDocumentor\Reflection\BaseReflector[] + * @param string $path */ - protected $location = array(); + public function setFilename( $path ) { + $this->path = $path; + } /** - * Last DocBlock associated with a non-documentable element. - * - * @var \PHPParser_Comment_Doc + * @return string */ - protected $last_doc = null; + public function getFilename() { + return $this->path; + } /** - * Add hooks to the queue and update the node stack when we enter a node. - * - * If we are entering a class, function or method, we push it to the location - * stack. This is just so that we know whether we are in the file scope or not, - * so that hooks in the main file scope can be added to the file. - * - * We also check function calls to see if there are any actions or hooks. If - * there are, they are added to the file's hooks if in the global scope, or if - * we are in a function/method, they are added to the queue. They will be - * assigned to the function by leaveNode(). We also check for any other function - * calls and treat them similarly, so that we can export a list of functions - * used by each element. - * - * Finally, we pick up any docblocks for nodes that usually aren't documentable, - * so they can be assigned to the hooks to which they may belong. - * - * @param \PHPParser_Node $node + * Parse and walk the file, populating the structural collections. */ - public function enterNode( \PHPParser_Node $node ) { - parent::enterNode( $node ); + public function process() { + $parser = ( new ParserFactory() )->createForNewestSupportedVersion(); - switch ( $node->getType() ) { - // Add classes, functions, and methods to the current location stack - case 'Stmt_Class': - case 'Stmt_Function': - case 'Stmt_ClassMethod': - array_push( $this->location, $node ); - break; + try { + $stmts = $parser->parse( $this->contents ); + } catch ( \PhpParser\Error $e ) { + $stmts = null; + } - // Parse out hook definitions and function calls and add them to the queue. - case 'Expr_FuncCall': - $function = new Function_Call_Reflector( $node, $this->context ); + if ( null === $stmts ) { + return; + } - // Add the call to the list of functions used in this scope. - $this->getLocation()->uses['functions'][] = $function; + $this->file_docblock = $this->detect_file_docblock( $stmts ); - if ( $this->isFilter( $node ) ) { - if ( $this->last_doc && ! $node->getDocComment() ) { - $node->setAttribute( 'comments', array( $this->last_doc ) ); - $this->last_doc = null; - } + // Pass 1: resolve names. replaceNodes:false keeps the original Name nodes and + // attaches resolved names as attributes — the v5 analog of v1's namespacedName. + $resolver = new NodeTraverser(); + $resolver->addVisitor( new NameResolver( null, array( 'replaceNodes' => false ) ) ); + $stmts = $resolver->traverse( $stmts ); - $hook = new Hook_Reflector( $node, $this->context ); + // Pass 2: fully-qualify class-position names (so a nested `Class::m()` caller + // prints as `\Class::m()`) while leaving function names alone, then reflect. + $traverser = new NodeTraverser(); + $traverser->addVisitor( new Class_Name_Resolver() ); + $traverser->addVisitor( $this ); + $traverser->traverse( $stmts ); + } - // Add it to the list of hooks used in this scope. - $this->getLocation()->uses['hooks'][] = $hook; - } - break; + /** + * Track scope, record hook/function/method usage, and carry hook docblocks. + * + * @param Node $node + * + * @return null + */ + public function enterNode( Node $node ) { + // Track namespace and import aliases. + if ( $node instanceof Node\Stmt\Namespace_ ) { + $this->namespace = $node->name ? $node->name->toString() : 'global'; + $this->aliases = array(); + } elseif ( $node instanceof Node\Stmt\Use_ ) { + foreach ( $node->uses as $use ) { + $this->aliases[ $use->getAlias()->toString() ] = $use->name->toString(); + } + } + + // Maintain the scope stack so calls are attributed to the right element. + if ( $node instanceof Node\Stmt\Function_ + || $node instanceof Node\Stmt\ClassMethod + || $node instanceof Node\Stmt\Class_ ) { + $this->location[] = $node; + } + + // Record function/method/hook usage. + if ( $node instanceof Node\Expr\FuncCall ) { + $this->add_use( 'functions', new Function_Call_Reflector( $node ) ); - // Parse out method calls, so we can export where methods are used. - case 'Expr_MethodCall': - $method = new Method_Call_Reflector( $node, $this->context ); + if ( $this->is_filter( $node ) ) { + if ( $this->last_doc && null === $node->getDocComment() ) { + $node->setAttribute( 'comments', array( $this->last_doc ) ); + $this->last_doc = null; + } - // Add it to the list of methods used in this scope. - $this->getLocation()->uses['methods'][] = $method; - break; + $this->add_use( 'hooks', new Hook_Reflector( $node, $this->namespace, $this->aliases ) ); + } + } elseif ( $node instanceof Node\Expr\MethodCall ) { + $this->add_use( 'methods', new Method_Call_Reflector( $node ) ); + } elseif ( $node instanceof Node\Expr\StaticCall ) { + $this->add_use( 'methods', new Static_Method_Call_Reflector( $node ) ); + } elseif ( $node instanceof Node\Expr\New_ ) { + $this->add_use( 'methods', new Method_Call_Reflector( $node ) ); + } - // Parse out method calls, so we can export where methods are used. - case 'Expr_StaticCall': - $method = new Static_Method_Call_Reflector( $node, $this->context ); + // Record file includes and constants (define() and the const keyword), + // captured wherever they appear — matching the legacy FileReflector output. + if ( $node instanceof Node\Expr\Include_ ) { + $this->add_include( $node ); + } elseif ( $node instanceof Node\Expr\FuncCall && $this->is_define( $node ) ) { + $this->add_define( $node ); + } elseif ( $node instanceof Node\Stmt\Const_ ) { + foreach ( $node->consts as $const ) { + $this->constants[] = new Constant_Reflector( + $const->name->toString(), + $const->getStartLine(), + Reflector_Helpers::default_value( $const->value ) + ); + } + } - // Add it to the list of methods used in this scope. - $this->getLocation()->uses['methods'][] = $method; - break; + // Carry a docblock from a non-documentable node to the next hook. + if ( ! $this->is_node_documentable( $node ) + && ! ( $node instanceof Node\Name ) + && null !== $node->getDocComment() ) { + $this->last_doc = $node->getDocComment(); + } - // Parse out `new Class()` calls as uses of Class::__construct(). - case 'Expr_New': - $method = new \WP_Parser\Method_Call_Reflector( $node, $this->context ); + return null; + } - // Add it to the list of methods used in this scope. - $this->getLocation()->uses['methods'][] = $method; - break; + /** + * Collect structural elements as the traverser leaves each node. + * + * Functions and classes are recorded on leave (not enter) so that nested + * functions are listed before their enclosing function — matching the order + * the legacy parser produced. Leaving a class is also when its methods' calls + * learn which class they were made in (for $this/self/parent resolution). + * + * @param Node $node + * + * @return null + */ + public function leaveNode( Node $node ) { + if ( $node instanceof Node\Stmt\Function_ ) { + $this->functions[] = new Function_Reflector( $node, $this->namespace, $this->aliases ); + } elseif ( $node instanceof Node\Stmt\Class_ && null !== $node->name ) { + $class = new Class_Reflector( $node, $this->namespace, $this->aliases ); + $this->set_called_in_class( $node, $class ); + $this->classes[] = $class; } - // Pick up DocBlock from non-documentable elements so that it can be assigned - // to the next hook if necessary. We don't do this for name nodes, since even - // though they aren't documentable, they still carry the docblock from their - // corresponding class/constant/function/etc. that they are the name of. If - // we don't ignore them, we'll end up picking up docblocks that are already - // associated with a named element, and so aren't really from a non- - // documentable element after all. - if ( ! $this->isNodeDocumentable( $node ) && 'Name' !== $node->getType() && ( $docblock = $node->getDocComment() ) ) { - $this->last_doc = $docblock; + if ( ! empty( $this->location ) && end( $this->location ) === $node ) { + array_pop( $this->location ); } + + return null; } /** - * Assign queued hooks to functions and update the node stack on leaving a node. + * Append a used element to the current scope (file, function, or method). * - * We can now access the function/method reflectors, so we can assign any queued - * hooks to them. The reflector for a node isn't created until the node is left. + * Method/function scope uses are stored on the scope node as an attribute, so + * the matching reflector picks them up when it is built. * - * @param \PHPParser_Node $node + * @param string $type Use type: functions, methods, or hooks. + * @param object $item The reflector for the used element. */ - public function leaveNode( \PHPParser_Node $node ) { + protected function add_use( $type, $item ) { + if ( empty( $this->location ) ) { + $this->uses[ $type ][] = $item; - parent::leaveNode( $node ); + return; + } - switch ( $node->getType() ) { - case 'Stmt_Class': - $class = end( $this->classes ); - if ( ! empty( $this->method_uses_queue ) ) { - /** @var Reflection\ClassReflector\MethodReflector $method */ - foreach ( $class->getMethods() as $method ) { - if ( isset( $this->method_uses_queue[ $method->getName() ] ) ) { - if ( isset( $this->method_uses_queue[ $method->getName() ]['methods'] ) ) { - /* - * For methods used in a class, set the class on the method call. - * That allows us to later get the correct class name for $this, self, parent. - */ - foreach ( $this->method_uses_queue[ $method->getName() ]['methods'] as $method_call ) { - /** @var Method_Call_Reflector $method_call */ - $method_call->set_class( $class ); - } - } + $scope = end( $this->location ); + $uses = $scope->getAttribute( 'wp_parser_uses', array() ); + $uses[ $type ][] = $item; + $scope->setAttribute( 'wp_parser_uses', $uses ); + } - $method->uses = $this->method_uses_queue[ $method->getName() ]; - } - } - } + /** + * Record an include/require statement with the legacy "Include"/"Require" (Once) + * type label. The name is the literal path when written as a plain string, or '' + * for a computed expression (e.g. dirname( __FILE__ ) . '/bootstrap.php'). + * + * @param Node\Expr\Include_ $node + */ + protected function add_include( Node\Expr\Include_ $node ) { + static $types = array( + Node\Expr\Include_::TYPE_INCLUDE => 'Include', + Node\Expr\Include_::TYPE_INCLUDE_ONCE => 'Include Once', + Node\Expr\Include_::TYPE_REQUIRE => 'Require', + Node\Expr\Include_::TYPE_REQUIRE_ONCE => 'Require Once', + ); - $this->method_uses_queue = array(); - array_pop( $this->location ); - break; + $this->includes[] = new Include_Reflector( + $node->expr instanceof Node\Scalar\String_ ? $node->expr->value : '', + $node->getStartLine(), + isset( $types[ $node->type ] ) ? $types[ $node->type ] : '' + ); + } - case 'Stmt_Function': - $function = array_pop( $this->location ); - if ( isset( $function->uses ) && ! empty( $function->uses ) ) { - end( $this->functions )->uses = $function->uses; - } - break; + /** + * Whether a function call is a call to define(). + * + * @param Node\Expr\FuncCall $node + * @return bool + */ + protected function is_define( Node\Expr\FuncCall $node ) { + return $node->name instanceof Node\Name + && 'define' === strtolower( ltrim( $node->name->toString(), '\\' ) ); + } + + /** + * Record a constant declared via define( 'NAME', value ). Only a string-literal + * name is recorded, matching the legacy parser (a computed name has no short name). + * + * @param Node\Expr\FuncCall $node + */ + protected function add_define( Node\Expr\FuncCall $node ) { + $args = $node->getArgs(); + + if ( ! isset( $args[0], $args[1] ) || ! ( $args[0]->value instanceof Node\Scalar\String_ ) ) { + return; + } + + $this->constants[] = new Constant_Reflector( + $args[0]->value->value, + $node->getStartLine(), + Reflector_Helpers::default_value( $args[1]->value ) + ); + } - case 'Stmt_ClassMethod': - $method = array_pop( $this->location ); + /** + * Tell each method call within a class which class it was made in. + * + * @param Node\Stmt\Class_ $node + * @param Class_Reflector $class + */ + protected function set_called_in_class( Node\Stmt\Class_ $node, Class_Reflector $class ) { + foreach ( $node->getMethods() as $method ) { + $uses = $method->getAttribute( 'wp_parser_uses' ); - /* - * Store the list of elements used by this method in the queue. We'll - * assign them to the method upon leaving the class (see above). - */ - if ( ! empty( $method->uses ) ) { - $this->method_uses_queue[ $method->name ] = $method->uses; + if ( empty( $uses['methods'] ) ) { + continue; + } + + foreach ( $uses['methods'] as $call ) { + if ( $call instanceof Method_Call_Reflector ) { + $call->set_class( $class ); } - break; + } } } /** - * @param \PHPParser_Node $node + * Whether a function call is a WordPress hook declaration. + * + * @param Node\Expr\FuncCall $node * * @return bool */ - protected function isFilter( \PHPParser_Node $node ) { - // Ignore variable functions - if ( 'Name' !== $node->name->getType() ) { + protected function is_filter( Node\Expr\FuncCall $node ) { + // A fully-qualified call (\do_action) is a plain function call, not a hook — + // the legacy parser only recognised the unqualified hook function names. + if ( ! ( $node->name instanceof Node\Name ) || $node->name instanceof Node\Name\FullyQualified ) { return false; } - $calling = (string) $node->name; - $functions = array( 'apply_filters', 'apply_filters_ref_array', @@ -213,24 +330,186 @@ protected function isFilter( \PHPParser_Node $node ) { 'do_action_deprecated', ); - return in_array( $calling, $functions ); + return in_array( (string) $node->name, $functions, true ); + } + + /** + * Whether a node is a documentable structural element (or a hook). + * + * @param Node $node + * + * @return bool + */ + protected function is_node_documentable( Node $node ) { + return $node instanceof Node\Stmt\Function_ + || $node instanceof Node\Stmt\ClassLike + || $node instanceof Node\Stmt\ClassMethod + || $node instanceof Node\Stmt\Property + || $node instanceof Node\Stmt\ClassConst + || $node instanceof Node\Stmt\Const_ + || ( $node instanceof Node\Expr\FuncCall && $this->is_filter( $node ) ); + } + + /** + * The file-level docblock, or null when the file has none. + * + * @return Docblock_Adapter|null + */ + public function getDocBlock() { + return $this->file_docblock; + } + + /** + * Detect the file-level docblock. + * + * The file docblock is the first docblock in the file, unless that docblock + * directly documents the first structural element (function/class/...). So a + * lone docblock before a `function`/`class` belongs to that element, but a + * docblock before a non-structural statement (or a second docblock preceding + * the first element) floats to the file. + * + * @param Node[] $stmts + * + * @return Docblock_Adapter|null + */ + protected function detect_file_docblock( array $stmts ) { + if ( empty( $stmts ) ) { + return null; + } + + $first = $stmts[0]; + + // A docblock attached before a `namespace` declaration is a file docblock. + if ( $first instanceof Node\Stmt\Namespace_ ) { + $docs = $this->doc_comments( $first ); + if ( ! empty( $docs ) ) { + return Docblock_Adapter::from_text( $docs[0]->getText(), 'global', array() ); + } + + if ( empty( $first->stmts ) ) { + return null; + } + + $first = $first->stmts[0]; + } + + $docs = $this->doc_comments( $first ); + + // Two docblocks before the first element: the first one is the file docblock. + if ( count( $docs ) >= 2 ) { + return Docblock_Adapter::from_text( $docs[0]->getText(), 'global', array() ); + } + + // A single docblock floats to the file only when it is attached to the open + // tag (no blank line after `claims_docblock( $first ) && $docs[0]->getStartLine() <= 2 ) { + return Docblock_Adapter::from_text( $docs[0]->getText(), 'global', array() ); + } + + return null; } /** - * @return File_Reflector + * The Doc comments attached to a node, in source order. + * + * @param Node $node + * + * @return Doc[] */ - protected function getLocation() { - return empty( $this->location ) ? $this : end( $this->location ); + protected function doc_comments( Node $node ) { + $docs = array(); + foreach ( $node->getComments() as $comment ) { + if ( $comment instanceof Doc ) { + $docs[] = $comment; + } + } + + return $docs; } /** - * @param \PHPParser_Node $node + * Whether the first statement in a file claims a preceding docblock as its own, + * keeping that docblock from floating up to become the file docblock. The legacy + * parser reflects — and so attaches the docblock to — structural elements + * (function/class/const), hooks, define() constants, and include/require, but + * not plain function calls or assignments. + * + * @param Node $node * * @return bool */ - protected function isNodeDocumentable( \PHPParser_Node $node ) { - return parent::isNodeDocumentable( $node ) - || ( $node instanceof \PHPParser_Node_Expr_FuncCall - && $this->isFilter( $node ) ); + protected function claims_docblock( Node $node ) { + if ( $node instanceof Node\Stmt\Function_ + || $node instanceof Node\Stmt\ClassLike + || $node instanceof Node\Stmt\Const_ ) { + return true; + } + + if ( $node instanceof Node\Stmt\Expression ) { + $expr = $node->expr; + + return $expr instanceof Node\Expr\Include_ + || ( $expr instanceof Node\Expr\FuncCall + && ( $this->is_filter( $expr ) || $this->is_define( $expr ) ) ); + } + + return false; + } + + /** + * @return Function_Reflector[] + */ + public function getFunctions() { + return $this->functions; + } + + /** + * @return Class_Reflector[] + */ + public function getClasses() { + return $this->classes; + } + + /** + * @return array + */ + public function getConstants() { + return $this->constants; + } + + /** + * @return array + */ + public function getIncludes() { + return $this->includes; + } +} + +/** + * Replaces class-position names (static calls, `new`, class-const/static-property + * fetches, instanceof) with their resolved fully-qualified form, so they render + * with a leading backslash when pretty printed — while leaving function/constant + * names untouched. This reproduces the legacy php-parser 1 name-resolution output. + */ +class Class_Name_Resolver extends NodeVisitorAbstract { + + public function enterNode( Node $node ) { + $is_class_ref = $node instanceof Node\Expr\StaticCall + || $node instanceof Node\Expr\New_ + || $node instanceof Node\Expr\ClassConstFetch + || $node instanceof Node\Expr\StaticPropertyFetch + || $node instanceof Node\Expr\Instanceof_; + + if ( $is_class_ref && $node->class instanceof Node\Name ) { + $resolved = $node->class->getAttribute( 'resolvedName' ); + if ( null !== $resolved ) { + $node->class = $resolved; + } + } + + return null; } } diff --git a/lib/class-function-call-reflector.php b/lib/class-function-call-reflector.php index 233e6d87..82bdbf3e 100644 --- a/lib/class-function-call-reflector.php +++ b/lib/class-function-call-reflector.php @@ -1,51 +1,64 @@ node = $node; + } /** - * Returns the name for this Reflector instance. + * The called function's name. + * + * Reproduces the legacy resolution: an unqualified name that resolves to itself + * is a global-fallback call and stays bare (count, apply_filters); a fully- + * qualified, qualified, or use-function-imported name resolves to a leading- + * backslash FQN (\do_action, \Other\helper, \My\Plugin\Sub\thing). * * @return string */ public function getName() { - if ( isset( $this->node->namespacedName ) ) { - return '\\' . implode( '\\', $this->node->namespacedName->parts ); - } + $name = $this->node->name; - $shortName = $this->getShortName(); + if ( $name instanceof Node\Name ) { + $resolved = $name->getAttribute( 'resolvedName' ); - if ( is_a( $shortName, 'PHPParser_Node_Name_FullyQualified' ) ) { - return '\\' . (string) $shortName; - } - - if ( is_a( $shortName, 'PHPParser_Node_Name' ) ) { - return (string) $shortName; - } + // Unqualified names resolving to themselves stay bare (global fallback). + if ( $name->isUnqualified() + && ( null === $resolved || $resolved->toString() === $name->toString() ) ) { + return $name->toString(); + } - /** @var \PHPParser_Node_Expr_ArrayDimFetch $shortName */ - if ( is_a( $shortName, 'PHPParser_Node_Expr_ArrayDimFetch' ) ) { - $var = $shortName->var->name; - $dim = $shortName->dim->name->parts[0]; + if ( null !== $resolved ) { + return '\\' . $resolved->toString(); + } - return "\${$var}[{$dim}]"; + return $name->toString(); } - /** @var \PHPParser_Node_Expr_Variable $shortName */ - if ( is_a( $shortName, 'PHPParser_Node_Expr_Variable' ) ) { - return $shortName->name; + if ( $name instanceof Node\Expr\Variable && is_string( $name->name ) ) { + return $name->name; } - return (string) $shortName; + return Reflector_Helpers::pretty_print_expr( $name ); + } + + public function getLineNumber() { + return $this->node->getStartLine(); + } + + public function getNode() { + return $this->node; } } diff --git a/lib/class-hook-reflector.php b/lib/class-hook-reflector.php index 50075e0f..d13dafcb 100644 --- a/lib/class-hook-reflector.php +++ b/lib/class-hook-reflector.php @@ -1,24 +1,39 @@ node = $node; + $this->namespace = $namespace; + $this->aliases = $aliases; + } /** * @return string */ public function getName() { - $printer = new PHPParser_PrettyPrinter_Default; + $printer = new Pretty_Printer(); + return $this->cleanupName( $printer->prettyPrintExpr( $this->node->args[0]->value ) ); } /** + * Normalize a hook name expression to the documented form. + * * @param string $name * * @return string @@ -60,6 +75,7 @@ public function getShortName() { */ public function getType() { $type = 'filter'; + switch ( (string) $this->node->name ) { case 'do_action': $type = 'action'; @@ -73,7 +89,7 @@ public function getType() { case 'apply_filters_ref_array': $type = 'filter_reference'; break; - case 'apply_filters_deprecated'; + case 'apply_filters_deprecated': $type = 'filter_deprecated'; break; } @@ -85,15 +101,31 @@ public function getType() { * @return array */ public function getArgs() { - $printer = new Pretty_Printer; + $printer = new Pretty_Printer(); $args = array(); + foreach ( $this->node->args as $arg ) { $args[] = $printer->prettyPrintArg( $arg ); } - // Skip the filter name + // Skip the hook name. array_shift( $args ); return $args; } + + public function getLineNumber() { + return $this->node->getStartLine(); + } + + public function getNode() { + return $this->node; + } + + /** + * @return Docblock_Adapter|null + */ + public function getDocBlock() { + return Docblock_Adapter::from_node( $this->node, $this->namespace, $this->aliases ); + } } diff --git a/lib/class-method-call-reflector.php b/lib/class-method-call-reflector.php index 570d5a54..5356c836 100644 --- a/lib/class-method-call-reflector.php +++ b/lib/class-method-call-reflector.php @@ -1,158 +1,179 @@ node = $node; + } + /** * Returns the name for this Reflector instance. * - * @return string[] Index 0 is the calling instance, 1 is the method name. + * @return string[] Index 0 is the calling instance/class, 1 is the method name. */ public function getName() { - - if ( 'Expr_New' === $this->node->getType() ) { - $name = '__construct'; + if ( $this->node instanceof Node\Expr\New_ ) { + $name = '__construct'; $caller = $this->node->class; } else { - $name = $this->getShortName(); + $name = $this->node->name instanceof Node\Identifier ? $this->node->name->toString() : ''; $caller = $this->node->var; } - if ( $caller instanceof \PHPParser_Node_Expr ) { - $printer = new Pretty_Printer; - $caller = $printer->prettyPrintExpr( $caller ); - } elseif ( $caller instanceof \PHPParser_Node_Name_FullyQualified ) { - $caller = '\\' . $caller->toString(); - } elseif ( $caller instanceof \PHPParser_Node_Name ) { - $caller = $caller->toString(); - } + return array( $this->resolve_caller( $caller ), $name ); + } - $caller = $this->_resolveName( $caller ); + /** + * Resolve a caller node to its legacy string form. + * + * @param Node $caller + * + * @return string + */ + protected function resolve_caller( $caller ) { + if ( $caller instanceof Node\Name ) { + $lower = strtolower( $caller->toString() ); - // If the caller is a function, convert it to the function name - if ( is_a( $caller, 'PHPParser_Node_Expr_FuncCall' ) ) { + if ( 'self' === $lower || 'parent' === $lower || 'static' === $lower ) { + return $this->map_class( $this->resolve_relative_name( $caller->toString() ) ); + } - // Add parentheses to signify this is a function call - /** @var \PHPParser_Node_Expr_FuncCall $caller */ - $caller = implode( '\\', $caller->name->parts ) . '()'; + return $this->map_class( Reflector_Helpers::class_name( $caller ) ); } - $class_mapping = $this->_getClassMapping(); - if ( array_key_exists( $caller, $class_mapping ) ) { - $caller = $class_mapping[ $caller ]; + if ( $caller instanceof Node\Expr ) { + return $this->map_class( $this->resolve_relative_name( Reflector_Helpers::pretty_print_expr( $caller ) ) ); } - return array( $caller, $name ); + // Anonymous class (e.g. `new class extends Foo {}`) — no name to report. + return ''; } /** - * Set the class that this method was called within. + * Apply the WordPress global-variable => class mapping. + * + * @param string $caller * - * @param ClassReflector $class + * @return string */ - public function set_class( ClassReflector $class ) { + protected function map_class( $caller ) { + $mapping = $this->get_class_mapping(); - $this->called_in_class = $class; + return array_key_exists( $caller, $mapping ) ? $mapping[ $caller ] : $caller; } /** - * Returns whether or not this method call is a static call + * Set the class that this method was called within. * - * @return bool Whether or not this method call is a static call + * @param Class_Reflector $class */ - public function isStatic() { - return false; + public function set_class( Class_Reflector $class ) { + $this->called_in_class = $class; } /** - * Returns a mapping from variable names to a class name, leverages globals for most used classes + * Whether this is a static call. * - * @return array Class mapping to map variable names to classes + * @return bool */ - protected function _getClassMapping() { - - // List of global use generated using following command: - // ack "global \\\$[^;]+;" --no-filename | tr -d '\t' | sort | uniq | sed "s/global //g" | sed "s/, /,/g" | tr , '\n' | sed "s/;//g" | sort | uniq | sed "s/\\\$//g" | sed "s/[^ ][^ ]*/'&' => ''/g" - // There is probably an easier way, there are currently no globals that are classes starting with an underscore - $wp_globals = array( - 'authordata' => 'WP_User', - 'custom_background' => 'Custom_Background', - 'custom_image_header' => 'Custom_Image_Header', - 'phpmailer' => 'PHPMailer', - 'post' => 'WP_Post', - 'userdata' => 'WP_User', // This can also be stdClass, but you can't call methods on an stdClass - 'wp' => 'WP', - 'wp_admin_bar' => 'WP_Admin_Bar', - 'wp_customize' => 'WP_Customize_Manager', - 'wp_embed' => 'WP_Embed', - 'wp_filesystem' => 'WP_Filesystem', - 'wp_hasher' => 'PasswordHash', // This can be overridden by plugins, for core assume this is ours - 'wp_json' => 'Services_JSON', - 'wp_list_table' => 'WP_List_Table', // This one differs because there are a lot of different List Tables, assume they all only overwrite existing functions on WP_List_Table - 'wp_locale' => 'WP_Locale', - 'wp_object_cache' => 'WP_Object_Cache', - 'wp_query' => 'WP_Query', - 'wp_rewrite' => 'WP_Rewrite', - 'wp_roles' => 'WP_Roles', - 'wp_scripts' => 'WP_Scripts', - 'wp_styles' => 'WP_Styles', - 'wp_the_query' => 'WP_Query', - 'wp_widget_factory' => 'WP_Widget_Factory', - 'wp_xmlrpc_server' => 'wp_xmlrpc_server', // This can be overridden by plugins, for core assume this is ours - 'wpdb' => 'wpdb', - ); - - $wp_functions = array( - 'get_current_screen()' => 'WP_Screen', - '_get_list_table()' => 'WP_List_Table', // This one differs because there are a lot of different List Tables, assume they all only overwrite existing functions on WP_List_Table - 'wp_get_theme()' => 'WP_Theme', - ); + public function isStatic() { + return false; + } - $class_mapping = array_merge( $wp_globals, $wp_functions ); + public function getLineNumber() { + return $this->node->getStartLine(); + } - return $class_mapping; + public function getNode() { + return $this->node; } /** - * Resolve a class name from self/parent. + * Resolve $this/self/parent against the enclosing class. * - * @param string $class The class name. + * @param string $class * - * @return string The resolved class name. + * @return string */ - protected function _resolveName( $class ) { - + protected function resolve_relative_name( $class ) { if ( ! $this->called_in_class ) { return $class; } - switch ( $class ) { case '$this': case 'self': $namespace = (string) $this->called_in_class->getNamespace(); $namespace = ( 'global' !== $namespace ) ? $namespace . '\\' : ''; - $class = '\\' . $namespace . $this->called_in_class->getShortName(); - break; + + return '\\' . $namespace . $this->called_in_class->getShortName(); + case 'parent': - $class = '\\' . $this->called_in_class->getNode()->extends->toString(); - break; + return $this->called_in_class->getParentClass(); } return $class; } + + /** + * A mapping from common WordPress global variable names to their class. + * + * @return array + */ + protected function get_class_mapping() { + $wp_globals = array( + 'authordata' => 'WP_User', + 'custom_background' => 'Custom_Background', + 'custom_image_header' => 'Custom_Image_Header', + 'phpmailer' => 'PHPMailer', + 'post' => 'WP_Post', + 'userdata' => 'WP_User', + 'wp' => 'WP', + 'wp_admin_bar' => 'WP_Admin_Bar', + 'wp_customize' => 'WP_Customize_Manager', + 'wp_embed' => 'WP_Embed', + 'wp_filesystem' => 'WP_Filesystem', + 'wp_hasher' => 'PasswordHash', + 'wp_json' => 'Services_JSON', + 'wp_list_table' => 'WP_List_Table', + 'wp_locale' => 'WP_Locale', + 'wp_object_cache' => 'WP_Object_Cache', + 'wp_query' => 'WP_Query', + 'wp_rewrite' => 'WP_Rewrite', + 'wp_roles' => 'WP_Roles', + 'wp_scripts' => 'WP_Scripts', + 'wp_styles' => 'WP_Styles', + 'wp_the_query' => 'WP_Query', + 'wp_widget_factory' => 'WP_Widget_Factory', + 'wp_xmlrpc_server' => 'wp_xmlrpc_server', + 'wpdb' => 'wpdb', + ); + + $wp_functions = array( + 'get_current_screen()' => 'WP_Screen', + '_get_list_table()' => 'WP_List_Table', + 'wp_get_theme()' => 'WP_Theme', + ); + + return array_merge( $wp_globals, $wp_functions ); + } } diff --git a/lib/class-pretty-printer.php b/lib/class-pretty-printer.php index 31c37e28..54a183e3 100644 --- a/lib/class-pretty-printer.php +++ b/lib/class-pretty-printer.php @@ -2,21 +2,32 @@ namespace WP_Parser; -use PHPParser_Node_Arg; -use PHPParser_PrettyPrinter_Default; +use PhpParser\Node; +use PhpParser\PrettyPrinter\Standard; /** - * Extends default printer for arguments. + * Extends the default pretty printer to render a single call argument. + * + * Used by the hook and method-call reflectors to turn argument and caller AST + * nodes back into source-equivalent strings. */ -class Pretty_Printer extends PHPParser_PrettyPrinter_Default { +class Pretty_Printer extends Standard { + /** - * Pretty prints an argument. + * Pretty print a single call argument. + * + * php-parser exposes no public method for a lone Arg node, so this mirrors + * prettyPrintExpr()'s state handling — reset + magic-token cleanup around the + * internal Arg printer. This replaces the v1 `noIndentToken` stripping, which + * no longer exists in php-parser 5. * - * @param PHPParser_Node_Arg $node Expression argument + * @param Node\Arg $node Argument node. * - * @return string Pretty printed argument + * @return string Pretty printed argument. */ - public function prettyPrintArg( PHPParser_Node_Arg $node ) { - return str_replace( "\n" . $this->noIndentToken, "\n", $this->p( $node ) ); + public function prettyPrintArg( Node\Arg $node ) { + $this->resetState(); + + return $this->handleMagicTokens( $this->p( $node ) ); } } diff --git a/lib/class-reflectors.php b/lib/class-reflectors.php new file mode 100644 index 00000000..ebf020cb --- /dev/null +++ b/lib/class-reflectors.php @@ -0,0 +1,545 @@ +prettyPrintExpr( $expr ); + } + + /** + * Render a default-value expression to its source string, or null when absent. + * + * @param Node\Expr|null $expr + * @return string|null + */ + public static function default_value( $expr ) { + if ( null === $expr ) { + return null; + } + + return self::printer()->prettyPrintExpr( $expr ); + } + + /** + * Render a type node to a string, or '' when there is no type. + * + * @param Node\Identifier|Node\Name|Node\ComplexType|null $type + * @return string + */ + public static function type_string( $type ) { + if ( null === $type ) { + return ''; + } + + if ( $type instanceof Node\NullableType ) { + return '?' . self::type_string( $type->type ); + } + + if ( $type instanceof Node\UnionType ) { + return implode( '|', array_map( array( self::class, 'type_string' ), $type->types ) ); + } + + if ( $type instanceof Node\IntersectionType ) { + return implode( '&', array_map( array( self::class, 'type_string' ), $type->types ) ); + } + + // Class-name types resolve to the legacy "\Fully\Qualified" form; built-in + // Identifier types (int, string, array, void, …) stay bare. + if ( $type instanceof Node\Name ) { + return self::class_name( $type ); + } + + return $type->toString(); + } + + /** + * Map php-parser visibility flags to a visibility string. + * + * @param Node\Stmt\ClassMethod|Node\Stmt\Property $node + * @return string + */ + public static function visibility( $node ) { + if ( $node->isPrivate() ) { + return 'private'; + } + + if ( $node->isProtected() ) { + return 'protected'; + } + + return 'public'; + } + + /** + * Resolve a class reference (extends/implements) to the legacy "\Fully\Qualified" + * form. Uses the NameResolver's resolvedName attribute when available so aliased + * and namespaced names resolve correctly; returns '' when there is no reference. + * + * @param Node\Name|null $name + * @return string + */ + public static function class_name( $name ) { + if ( null === $name ) { + return ''; + } + + $resolved = $name->getAttribute( 'resolvedName' ); + + return '\\' . ( $resolved ? $resolved->toString() : $name->toString() ); + } + + /** + * Render a namespace-alias map for export, fully-qualifying each target with a + * leading backslash (alias => "\Fully\Qualified"), matching the legacy output. + * + * @param array $aliases Map of alias => fully-qualified name (no leading slash). + * @return array + */ + public static function export_aliases( array $aliases ) { + $out = array(); + foreach ( $aliases as $alias => $fqn ) { + $out[ $alias ] = '\\' . ltrim( $fqn, '\\' ); + } + + return $out; + } + + /** + * Build Argument_Reflectors for a list of parameters. + * + * @param Node\Param[] $params + * @return Argument_Reflector[] + */ + public static function arguments( array $params ) { + $arguments = array(); + foreach ( $params as $param ) { + $arguments[] = new Argument_Reflector( $param ); + } + + return $arguments; + } +} + +/** + * A function argument (parameter). + */ +class Argument_Reflector { + + /** @var Node\Param */ + protected $node; + + public function __construct( Node\Param $node ) { + $this->node = $node; + } + + public function getName() { + return '$' . ( is_string( $this->node->var->name ) ? $this->node->var->name : '' ); + } + + public function getDefault() { + return Reflector_Helpers::default_value( $this->node->default ); + } + + public function getType() { + return Reflector_Helpers::type_string( $this->node->type ); + } + + public function getNode() { + return $this->node; + } +} + +/** + * A top-level (or namespaced) function definition. + */ +class Function_Reflector { + + /** @var array|null Elements used by this function; populated during traversal. */ + public $uses; + + /** @var Node\Stmt\Function_ */ + protected $node; + protected $namespace; + protected $aliases; + + public function __construct( Node\Stmt\Function_ $node, $namespace, array $aliases ) { + $this->node = $node; + $this->namespace = $namespace; + $this->aliases = $aliases; + $this->uses = $node->getAttribute( 'wp_parser_uses' ); + } + + public function getShortName() { + return $this->node->name->toString(); + } + + public function getNamespace() { + return $this->namespace; + } + + /** + * The namespace aliases in scope, fully-qualified for export (alias => "\FQN"). + * + * @return array + */ + public function getNamespaceAliases() { + return Reflector_Helpers::export_aliases( $this->aliases ); + } + + public function getLineNumber() { + return $this->node->getStartLine(); + } + + public function getNode() { + return $this->node; + } + + public function getArguments() { + return Reflector_Helpers::arguments( $this->node->params ); + } + + public function getDocBlock() { + return Docblock_Adapter::from_node( $this->node, $this->namespace, $this->aliases ); + } +} + +/** + * A class definition. + */ +class Class_Reflector { + + /** @var Node\Stmt\Class_ */ + protected $node; + protected $namespace; + protected $aliases; + + public function __construct( Node\Stmt\Class_ $node, $namespace, array $aliases ) { + $this->node = $node; + $this->namespace = $namespace; + $this->aliases = $aliases; + } + + public function getShortName() { + return $this->node->name->toString(); + } + + public function getNamespace() { + return $this->namespace; + } + + public function getNamespaceAliases() { + return $this->aliases; + } + + public function getLineNumber() { + return $this->node->getStartLine(); + } + + public function getNode() { + return $this->node; + } + + public function isFinal() { + return $this->node->isFinal(); + } + + public function isAbstract() { + return $this->node->isAbstract(); + } + + public function getParentClass() { + return Reflector_Helpers::class_name( $this->node->extends ); + } + + public function getInterfaces() { + $interfaces = array(); + foreach ( $this->node->implements as $interface ) { + $interfaces[] = Reflector_Helpers::class_name( $interface ); + } + + return $interfaces; + } + + public function getProperties() { + $properties = array(); + foreach ( $this->node->getProperties() as $property ) { + foreach ( $property->props as $prop ) { + $properties[] = new Property_Reflector( $property, $prop, $this->namespace, $this->aliases ); + } + } + + return $properties; + } + + public function getMethods() { + $methods = array(); + foreach ( $this->node->getMethods() as $method ) { + $methods[] = new Method_Reflector( $method, $this->namespace, $this->aliases ); + } + + return $methods; + } + + public function getDocBlock() { + return Docblock_Adapter::from_node( $this->node, $this->namespace, $this->aliases ); + } +} + +/** + * A class method. + */ +class Method_Reflector { + + /** @var array|null Elements used by this method; populated during traversal. */ + public $uses; + + /** @var Node\Stmt\ClassMethod */ + protected $node; + + /** @var string Namespace used to resolve docblock types (the class's namespace). */ + protected $resolve_namespace; + protected $aliases; + + public function __construct( Node\Stmt\ClassMethod $node, $resolve_namespace, array $aliases ) { + $this->node = $node; + $this->resolve_namespace = $resolve_namespace; + $this->aliases = $aliases; + $this->uses = $node->getAttribute( 'wp_parser_uses' ); + } + + public function getShortName() { + return $this->node->name->toString(); + } + + /** + * The method's enclosing namespace. The global namespace is reported as '' + * (not 'global', unlike functions and classes), matching the legacy output. + * + * @return string + */ + public function getNamespace() { + return 'global' === $this->resolve_namespace ? '' : $this->resolve_namespace; + } + + /** + * The namespace aliases in scope, fully-qualified for export (alias => "\FQN"). + * + * @return array + */ + public function getNamespaceAliases() { + return Reflector_Helpers::export_aliases( $this->aliases ); + } + + public function getLineNumber() { + return $this->node->getStartLine(); + } + + public function getNode() { + return $this->node; + } + + public function isFinal() { + return $this->node->isFinal(); + } + + public function isAbstract() { + return $this->node->isAbstract(); + } + + public function isStatic() { + return $this->node->isStatic(); + } + + public function getVisibility() { + return Reflector_Helpers::visibility( $this->node ); + } + + public function getArguments() { + return Reflector_Helpers::arguments( $this->node->params ); + } + + public function getDocBlock() { + return Docblock_Adapter::from_node( $this->node, $this->resolve_namespace, $this->aliases ); + } +} + +/** + * A single declared class property. + */ +class Property_Reflector { + + /** @var Node\Stmt\Property */ + protected $stmt; + + /** @var Node\PropertyItem|object The individual property within the declaration. */ + protected $prop; + + protected $namespace; + protected $aliases; + + public function __construct( Node\Stmt\Property $stmt, $prop, $namespace, array $aliases ) { + $this->stmt = $stmt; + $this->prop = $prop; + $this->namespace = $namespace; + $this->aliases = $aliases; + } + + public function getName() { + return '$' . $this->prop->name->toString(); + } + + public function getLineNumber() { + return $this->stmt->getStartLine(); + } + + public function getNode() { + return $this->stmt; + } + + public function getDefault() { + return Reflector_Helpers::default_value( $this->prop->default ); + } + + public function isStatic() { + return $this->stmt->isStatic(); + } + + public function getVisibility() { + return Reflector_Helpers::visibility( $this->stmt ); + } + + public function getDocBlock() { + return Docblock_Adapter::from_node( $this->stmt, $this->namespace, $this->aliases ); + } +} + +/** + * Reflects a single include/require statement, exposing the surface runner.php + * reads. The type is the legacy human-readable label ("Include", "Require Once"). + */ +class Include_Reflector { + + /** @var string The included path when written as a string literal, or '' when computed. */ + protected $name; + + /** @var int The line the include/require appears on. */ + protected $line; + + /** @var string The legacy type label ("Include", "Include Once", "Require", "Require Once"). */ + protected $type; + + /** + * @param string $name The included path, or '' for a computed expression. + * @param int $line The line number. + * @param string $type The legacy type label. + */ + public function __construct( $name, $line, $type ) { + $this->name = $name; + $this->line = $line; + $this->type = $type; + } + + /** + * @return string + */ + public function getName() { + return $this->name; + } + + /** + * @return int + */ + public function getLineNumber() { + return $this->line; + } + + /** + * @return string + */ + public function getType() { + return $this->type; + } +} + +/** + * Reflects a file-level constant — a define() call or the const keyword — exposing + * the surface runner.php reads. The value is the pretty-printed default expression. + */ +class Constant_Reflector { + + /** @var string The constant's name (the define() name, or the const keyword identifier). */ + protected $name; + + /** @var int The line the constant is declared on. */ + protected $line; + + /** @var string The pretty-printed value expression. */ + protected $value; + + /** + * @param string $name The constant name. + * @param int $line The line number. + * @param string $value The pretty-printed value expression. + */ + public function __construct( $name, $line, $value ) { + $this->name = $name; + $this->line = $line; + $this->value = $value; + } + + /** + * @return string + */ + public function getShortName() { + return $this->name; + } + + /** + * @return int + */ + public function getLineNumber() { + return $this->line; + } + + /** + * @return string + */ + public function getValue() { + return $this->value; + } +} diff --git a/lib/class-static-method-call-reflector.php b/lib/class-static-method-call-reflector.php index 9d39c7c2..9058cc06 100644 --- a/lib/class-static-method-call-reflector.php +++ b/lib/class-static-method-call-reflector.php @@ -1,10 +1,14 @@ node->class; - $prefix = ( is_a( $class, 'PHPParser_Node_Name_FullyQualified' ) ) ? '\\' : ''; - $class = $prefix . $this->_resolveName( implode( '\\', $class->parts ) ); + $method = $this->node->name instanceof Node\Identifier ? $this->node->name->toString() : ''; - return array( $class, $this->getShortName() ); + return array( $this->resolve_caller( $this->node->class ), $method ); } /** diff --git a/lib/runner.php b/lib/runner.php index ba3efdd4..56883943 100644 --- a/lib/runner.php +++ b/lib/runner.php @@ -2,13 +2,6 @@ namespace WP_Parser; -use phpDocumentor\Reflection\BaseReflector; -use phpDocumentor\Reflection\ClassReflector\MethodReflector; -use phpDocumentor\Reflection\ClassReflector\PropertyReflector; -use phpDocumentor\Reflection\FunctionReflector; -use phpDocumentor\Reflection\FunctionReflector\ArgumentReflector; -use phpDocumentor\Reflection\ReflectionAbstract; - /** * @param string $directory * @@ -189,7 +182,7 @@ function ( $matches ) use ( $replacement_string ) { } /** - * @param BaseReflector|ReflectionAbstract $element + * @param object $element A reflector exposing getDocBlock(). * * @return array */ @@ -269,7 +262,7 @@ function export_hooks( array $hooks ) { } /** - * @param ArgumentReflector[] $arguments + * @param Argument_Reflector[] $arguments * * @return array */ @@ -288,7 +281,7 @@ function export_arguments( array $arguments ) { } /** - * @param PropertyReflector[] $properties + * @param Property_Reflector[] $properties * * @return array */ @@ -312,7 +305,7 @@ function export_properties( array $properties ) { } /** - * @param MethodReflector[] $methods + * @param Method_Reflector[] $methods * * @return array */ @@ -366,7 +359,7 @@ function export_uses( array $uses ) { foreach ( $uses as $type => $used_elements ) { - /** @var MethodReflector|FunctionReflector $element */ + /** @var Method_Call_Reflector|Function_Call_Reflector $element */ foreach ( $used_elements as $element ) { $name = $element->getName(); diff --git a/phpunit-golden.xml.dist b/phpunit-golden.xml.dist new file mode 100644 index 00000000..6972c11a --- /dev/null +++ b/phpunit-golden.xml.dist @@ -0,0 +1,20 @@ + + + + + + tests/golden + + + diff --git a/phpunit-unit.xml.dist b/phpunit-unit.xml.dist new file mode 100644 index 00000000..97b77933 --- /dev/null +++ b/phpunit-unit.xml.dist @@ -0,0 +1,23 @@ + + + + + + tests/unit + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 71b40114..6285bf81 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,4 +1,7 @@ + - - + + lib - - + + diff --git a/plugin.php b/plugin.php index e858aec4..2fb63e11 100644 --- a/plugin.php +++ b/plugin.php @@ -20,8 +20,15 @@ $wp_parser->on_load(); } -register_activation_hook( __FILE__, array( 'P2P_Storage', 'init' ) ); -register_activation_hook( __FILE__, array( 'P2P_Storage', 'install' ) ); +register_activation_hook( __FILE__, function () { + // The bundled Posts-to-Posts library may not be loaded at activation time + // (e.g. before Composer dependencies are installed in CI). Relationships also + // creates these tables on demand, so guard against a fatal here. + if ( class_exists( 'P2P_Storage' ) ) { + \P2P_Storage::init(); + \P2P_Storage::install(); + } +} ); // TODO safer handling for uninstall //register_uninstall_hook( __FILE__, array( 'P2P_Storage', 'uninstall' ) ); diff --git a/tests/golden/README.md b/tests/golden/README.md new file mode 100644 index 00000000..704fbfc0 --- /dev/null +++ b/tests/golden/README.md @@ -0,0 +1,66 @@ +# Golden-master parser harness + +A **characterization test** that pins the parser's output so it can be rewritten +safely. The plugin's whole downstream (the importer, the DevHub theme, the +Posts-to-Posts relationships) consumes only the array returned by +`WP_Parser\parse_files()`. The migration's #1 rule is that the rewritten parser +must reproduce that array **key-for-key**. + +This harness captures the **old** parser's output as JSON snapshots, then asserts +the current parser still matches them. + +``` +bin/generate-golden.php # writes snapshots from the installed parser +tests/golden/golden.php # shared corpus + normalize + JSON helpers +tests/golden/bootstrap.php # standalone (WordPress-free) PHPUnit bootstrap +tests/golden/test-golden-master.php # the comparison test +tests/golden/snapshots/*.json # committed baseline (the oracle) +phpunit-golden.xml.dist # config for this suite +``` + +The corpus is every `tests/source/*.php` fixture plus every `tests/**/*.inc` +fixture, each parsed on its own. `parse_files()` is WordPress-free, so nothing +here needs the WP test framework. + +## 1. Capture the baseline (do this once, on the OLD stack) + +The old stack (`phpdocumentor/reflection ~3.0`, `nikic/php-parser 1.x`) floods +PHP 8.x with deprecations/warnings and produces unreliable output, so the +baseline must be captured on **PHP 7.4**. The installed `vendor/` is pure PHP and +version-agnostic, so install it on the host and just run the generator inside a +PHP 7.4 container: + +```bash +composer install --no-dev # installs the OLD locked parser stack (skips phpunit ^7) +bin/generate-golden-docker.sh # runs bin/generate-golden.php inside php:7.4-cli +``` + +The helper reuses the host's `vendor/` and writes files as your user (no Docker +root-owned files). It is equivalent to: + +```bash +docker run --rm -u "$(id -u):$(id -g)" -e HOME=/tmp -v "$PWD":/app -w /app \ + php:7.4-cli php bin/generate-golden.php +``` + +Then commit `tests/golden/snapshots/`. **Never regenerate these against the new +parser** — that would erase the oracle. + +## 2. Run the test (any time, on whatever stack is installed) + +```bash +composer install +./vendor/bin/phpunit -c phpunit-golden.xml.dist +``` + +- On the **old** stack the test passes trivially (parser vs. its own snapshot) — + this proves the harness is wired correctly before any code changes. +- During the **rewrite** every dropped field, changed type, or reordered entry + becomes a hard failure naming the exact fixture. + +## 3. Intentional changes + +If the rewrite *intentionally* changes output (e.g. adding class-constant +visibility, issue #224), regenerate the affected snapshot, **review the JSON diff +in the commit**, and call it out in the PR. The snapshots are reviewed artifacts, +not throwaway fixtures. diff --git a/tests/golden/bootstrap.php b/tests/golden/bootstrap.php new file mode 100644 index 00000000..fac9e748 --- /dev/null +++ b/tests/golden/bootstrap.php @@ -0,0 +1,20 @@ + Map of slug => entry. + */ +function corpus() { + $root = repo_root(); + $files = array(); + + foreach ( (array) glob( $root . '/tests/source/*.php' ) as $file ) { + $files[] = $file; + } + + $dir = $root . '/tests/phpunit/tests'; + if ( is_dir( $dir ) ) { + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator( $dir, \FilesystemIterator::SKIP_DOTS ) + ); + foreach ( $iterator as $file ) { + if ( 'inc' === $file->getExtension() ) { + $files[] = $file->getPathname(); + } + } + } + + sort( $files ); // Deterministic ordering across platforms. + + $corpus = array(); + foreach ( $files as $file ) { + $corpus[ slug( $file ) ] = array( + 'files' => array( $file ), + 'root' => dirname( $file ), + ); + } + + return $corpus; +} + +/** + * Turn a fixture path into a stable, filesystem-safe snapshot slug. + * + * @param string $file Absolute path to a fixture. + * @return string + */ +function slug( $file ) { + $relative = ltrim( str_replace( repo_root(), '', $file ), '/\\' ); + $relative = preg_replace( '#^tests/phpunit/tests/#', '', $relative ); + $relative = preg_replace( '#^tests/#', '', $relative ); + $relative = preg_replace( '#\.(php|inc)$#', '', $relative ); + + return str_replace( array( '/', '\\' ), '__', $relative ); +} + +/** + * Absolute path to a slug's snapshot file. + * + * @param string $slug Snapshot slug. + * @return string + */ +function snapshot_path( $slug ) { + return snapshots_dir() . '/' . $slug . '.json'; +} + +/** + * Scrub environment-specific values from parser output so it is reproducible. + * + * Only the absolute root path is scrubbed; every other value is content-derived + * and must match exactly between the old and rewritten parsers. + * + * @param array $data Output of parse_files(). + * @return array + */ +function normalize( array $data ) { + foreach ( $data as &$file ) { + if ( isset( $file['root'] ) ) { + $file['root'] = ROOT_PLACEHOLDER; + } + } + unset( $file ); + + return $data; +} + +/** + * Canonical JSON encoding shared by the generator and the test, so the comparison + * is byte-for-byte. + * + * @param mixed $data Data to encode. + * @return string + */ +function to_json( $data ) { + return json_encode( + $data, + JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE + ) . "\n"; +} + +/** + * Parse a corpus entry and return its normalized output array. + * + * @param array{files:string[],root:string} $entry Corpus entry. + * @return array + */ +function parse_entry( array $entry ) { + return normalize( \WP_Parser\parse_files( $entry['files'], $entry['root'] ) ); +} diff --git a/tests/golden/snapshots/export__class-features.json b/tests/golden/snapshots/export__class-features.json new file mode 100644 index 00000000..1fc7f64d --- /dev/null +++ b/tests/golden/snapshots/export__class-features.json @@ -0,0 +1,161 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "class-features.inc", + "root": "{{ROOT}}", + "classes": [ + { + "name": "Base_Thing", + "namespace": "global", + "line": 6, + "end_line": 24, + "final": false, + "abstract": true, + "extends": "\\Parent_Thing", + "implements": [ + "\\Interface_One", + "\\Interface_Two" + ], + "properties": [ + { + "name": "$static_prop", + "line": 9, + "end_line": 9, + "default": "array()", + "static": true, + "visibility": "public", + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "$untyped_prop", + "line": 10, + "end_line": 10, + "default": null, + "static": false, + "visibility": "protected", + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "$a", + "line": 11, + "end_line": 11, + "default": null, + "static": false, + "visibility": "private", + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "$b", + "line": 11, + "end_line": 11, + "default": "'x'", + "static": false, + "visibility": "private", + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + } + ], + "methods": [ + { + "name": "must_implement", + "namespace": "", + "aliases": [], + "line": 16, + "end_line": 16, + "final": false, + "abstract": true, + "static": false, + "visibility": "public", + "arguments": [ + { + "name": "$arg", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Must be implemented.", + "long_description": "", + "tags": [] + } + }, + { + "name": "cannot_override", + "namespace": "", + "aliases": [], + "line": 21, + "end_line": 21, + "final": true, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [], + "doc": { + "description": "Cannot be overridden.", + "long_description": "", + "tags": [] + } + }, + { + "name": "static_method", + "namespace": "", + "aliases": [], + "line": 23, + "end_line": 23, + "final": false, + "abstract": false, + "static": true, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + } + ], + "doc": { + "description": "Class features fixture.", + "long_description": "", + "tags": [] + } + }, + { + "name": "Final_Thing", + "namespace": "global", + "line": 26, + "end_line": 28, + "final": true, + "abstract": false, + "extends": "", + "implements": [], + "properties": [], + "methods": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + } + ] + } +] diff --git a/tests/golden/snapshots/export__closures.json b/tests/golden/snapshots/export__closures.json new file mode 100644 index 00000000..accc9740 --- /dev/null +++ b/tests/golden/snapshots/export__closures.json @@ -0,0 +1,64 @@ +[ + { + "file": { + "description": "Closures and callables.", + "long_description": "", + "tags": [] + }, + "path": "closures.inc", + "root": "{{ROOT}}", + "uses": { + "functions": [ + { + "name": "add_action", + "line": 6, + "end_line": 9 + }, + { + "name": "do_something", + "line": 7, + "end_line": 7 + }, + { + "name": "helper", + "line": 12, + "end_line": 12 + } + ], + "methods": [ + { + "name": "method", + "class": "$obj", + "static": false, + "line": 8, + "end_line": 8 + } + ] + }, + "functions": [ + { + "name": "with_closure", + "namespace": "global", + "aliases": [], + "line": 15, + "end_line": 21, + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [], + "uses": { + "functions": [ + { + "name": "inner_call", + "line": 17, + "end_line": 17 + } + ] + } + } + ] + } +] diff --git a/tests/golden/snapshots/export__constants-includes.json b/tests/golden/snapshots/export__constants-includes.json new file mode 100644 index 00000000..4ed5bc8d --- /dev/null +++ b/tests/golden/snapshots/export__constants-includes.json @@ -0,0 +1,123 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "constants-includes.inc", + "root": "{{ROOT}}", + "uses": { + "functions": [ + { + "name": "dirname", + "line": 6, + "end_line": 6 + }, + { + "name": "define", + "line": 11, + "end_line": 11 + }, + { + "name": "define", + "line": 12, + "end_line": 12 + } + ] + }, + "includes": [ + { + "name": "", + "line": 6, + "type": "Require Once" + }, + { + "name": "helper.php", + "line": 7, + "type": "Include" + }, + { + "name": "", + "line": 8, + "type": "Require" + }, + { + "name": "once.php", + "line": 9, + "type": "Include Once" + }, + { + "name": "inside-function.php", + "line": 19, + "type": "Include" + } + ], + "constants": [ + { + "name": "MY_CONSTANT", + "line": 11, + "value": "42" + }, + { + "name": "MY_STRING", + "line": 12, + "value": "'hello'" + }, + { + "name": "MY_CONST_KEYWORD", + "line": 13, + "value": "'world'" + }, + { + "name": "INSIDE_FUNC", + "line": 20, + "value": "true" + } + ], + "functions": [ + { + "name": "uses_constant", + "namespace": "global", + "aliases": [], + "line": 18, + "end_line": 22, + "arguments": [], + "doc": { + "description": "A function with an include and a define inside.", + "long_description": "", + "tags": [] + }, + "hooks": [], + "uses": { + "functions": [ + { + "name": "define", + "line": 20, + "end_line": 20 + } + ] + } + } + ], + "classes": [ + { + "name": "Has_Const", + "namespace": "global", + "line": 27, + "end_line": 29, + "final": false, + "abstract": false, + "extends": "", + "implements": [], + "properties": [], + "methods": [], + "doc": { + "description": "A class with a constant.", + "long_description": "", + "tags": [] + } + } + ] + } +] diff --git a/tests/golden/snapshots/export__docblocks.json b/tests/golden/snapshots/export__docblocks.json new file mode 100644 index 00000000..58d9a323 --- /dev/null +++ b/tests/golden/snapshots/export__docblocks.json @@ -0,0 +1,391 @@ +[ + { + "file": { + "description": "This is the file-level docblock summary.", + "long_description": "

This is the file-level docblock description, which may span multiple lines. In fact, this one does. It spans more than two full lines, continuing on to the third line.

", + "tags": [ + { + "name": "since", + "content": "1.5.0" + } + ] + }, + "path": "docblocks.inc", + "root": "{{ROOT}}", + "uses": { + "functions": [ + { + "name": "apply_filters", + "line": 100, + "end_line": 100 + }, + { + "name": "apply_filters_ref_array", + "line": 105, + "end_line": 105 + }, + { + "name": "do_action", + "line": 114, + "end_line": 114 + }, + { + "name": "do_action", + "line": 116, + "end_line": 116 + }, + { + "name": "do_action_ref_array", + "line": 121, + "end_line": 121 + } + ] + }, + "hooks": [ + { + "name": "test_filter", + "line": 100, + "end_line": 100, + "type": "filter", + "arguments": [ + "$var" + ], + "doc": { + "description": "A filter.", + "long_description": "", + "tags": [] + } + }, + { + "name": "test_ref_array_filter", + "line": 105, + "end_line": 105, + "type": "filter_reference", + "arguments": [ + "array(&$var)" + ], + "doc": { + "description": "A reference array filter.", + "long_description": "", + "tags": [] + } + }, + { + "name": "test_action", + "line": 114, + "end_line": 114, + "type": "action", + "arguments": [ + "$post" + ], + "doc": { + "description": "A test action.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "3.7.0" + }, + { + "name": "param", + "content": "Post object.", + "types": [ + "\\WP_Post" + ], + "variable": "$post" + } + ] + } + }, + { + "name": "undocumented_hook", + "line": 116, + "end_line": 116, + "type": "action", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "test_ref_array_action", + "line": 121, + "end_line": 121, + "type": "action_reference", + "arguments": [ + "array(&$var)" + ], + "doc": { + "description": "A reference array action.", + "long_description": "", + "tags": [] + } + } + ], + "functions": [ + { + "name": "test_func", + "namespace": "global", + "aliases": [], + "line": 25, + "end_line": 29, + "arguments": [ + { + "name": "$var", + "default": null, + "type": "" + }, + { + "name": "$num", + "default": null, + "type": "" + } + ], + "doc": { + "description": "This is a function docblock.", + "long_description": "

This function is just a test, but we've added this description anyway.

", + "tags": [ + { + "name": "since", + "content": "2.6.0" + }, + { + "name": "param", + "content": "A string value.", + "types": [ + "string" + ], + "variable": "$var" + }, + { + "name": "param", + "content": "A number.", + "types": [ + "int" + ], + "variable": "$num" + }, + { + "name": "return", + "content": "Whether the function was called correctly.", + "types": [ + "bool" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "test_markdown_in_description", + "namespace": "global", + "aliases": [], + "line": 144, + "end_line": 146, + "arguments": [], + "doc": { + "description": "This method has all kinds of _\"markdown formatting\"_, many of which are not supported.", + "long_description": "

Example code:

foo();\nbar();

Lists:

This is a quote:

Quoted Content.
On multiple lines.

Inline Formatting includes Headings.

There's also italics and bold.

", + "tags": [] + }, + "hooks": [] + } + ], + "classes": [ + { + "name": "Test_Class", + "namespace": "global", + "line": 40, + "end_line": 95, + "final": false, + "abstract": false, + "extends": "", + "implements": [], + "properties": [ + { + "name": "$a_string", + "line": 49, + "end_line": 49, + "default": null, + "static": false, + "visibility": "public", + "doc": { + "description": "This is a docblock for a class property.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "3.0.0" + }, + { + "name": "var", + "content": "", + "types": [ + "string" + ], + "variable": "" + } + ] + } + } + ], + "methods": [ + { + "name": "test_method", + "namespace": "", + "aliases": [], + "line": 61, + "end_line": 63, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [ + { + "name": "$var", + "default": null, + "type": "" + }, + { + "name": "$arr", + "default": null, + "type": "" + } + ], + "doc": { + "description": "This is a method docblock.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "4.5.0" + }, + { + "name": "param", + "content": "A parameter.", + "types": [ + "mixed" + ], + "variable": "$var" + }, + { + "name": "param", + "content": "Another parameter.", + "types": [ + "array" + ], + "variable": "$arr" + }, + { + "name": "return", + "content": "The first param.", + "types": [ + "mixed" + ] + } + ] + } + }, + { + "name": "test_method_see", + "namespace": "", + "aliases": [], + "line": 71, + "end_line": 72, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [], + "doc": { + "description": "A test method that contains various @see.", + "long_description": "", + "tags": [ + { + "name": "see", + "content": "", + "refers": "self::test_method_typed_hash()" + }, + { + "name": "see", + "content": "The WordPress Foundation.", + "refers": "https://wordpressfoundation.org/" + } + ] + } + }, + { + "name": "test_method_typed_hash", + "namespace": "", + "aliases": [], + "line": 91, + "end_line": 93, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [ + { + "name": "$hashed_array", + "default": null, + "type": "array" + }, + { + "name": "$post_or_user", + "default": null, + "type": "" + }, + { + "name": "$nullable_post", + "default": null, + "type": "\\WP_Post" + } + ], + "doc": { + "description": "This is a method that has a param with multiple types within.", + "long_description": "", + "tags": [ + { + "name": "param", + "content": "{ The parameters for this function.
@type int $time The current epoch.
@type ?string $nullable_string A nullable string.
@type string|array List of items:
- 'item1'
- 'item2' Default is 'item1'.
}", + "types": [ + "array" + ], + "variable": "$hashed_array" + }, + { + "name": "param", + "content": "A Post or User.", + "types": [ + "\\WP_Post", + "\\WP_User" + ], + "variable": "$post_or_user" + }, + { + "name": "return", + "content": "An empty array.", + "types": [ + "array" + ] + } + ] + } + } + ], + "doc": { + "description": "This is a class docblock.", + "long_description": "

This is the more wordy description: This is a comment with two *'s at the start, which means that it is a doc comment. Docblock comments are comment blocks used to document code. This one documents the Test_Class class.

", + "tags": [ + { + "name": "since", + "content": "3.5.2" + } + ] + } + } + ] + } +] diff --git a/tests/golden/snapshots/export__hooks-extra.json b/tests/golden/snapshots/export__hooks-extra.json new file mode 100644 index 00000000..8dbfccfc --- /dev/null +++ b/tests/golden/snapshots/export__hooks-extra.json @@ -0,0 +1,244 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "hooks-extra.inc", + "root": "{{ROOT}}", + "uses": { + "functions": [ + { + "name": "do_action", + "line": 6, + "end_line": 6 + }, + { + "name": "do_action", + "line": 7, + "end_line": 7 + }, + { + "name": "do_action_ref_array", + "line": 8, + "end_line": 8 + }, + { + "name": "do_action_deprecated", + "line": 9, + "end_line": 9 + }, + { + "name": "apply_filters", + "line": 11, + "end_line": 11 + }, + { + "name": "apply_filters", + "line": 12, + "end_line": 12 + }, + { + "name": "apply_filters_ref_array", + "line": 13, + "end_line": 13 + }, + { + "name": "apply_filters_deprecated", + "line": 14, + "end_line": 14 + }, + { + "name": "apply_filters", + "line": 16, + "end_line": 16 + }, + { + "name": "do_action", + "line": 18, + "end_line": 18 + }, + { + "name": "do_action", + "line": 19, + "end_line": 19 + }, + { + "name": "do_action", + "line": 20, + "end_line": 20 + } + ] + }, + "hooks": [ + { + "name": "simple_action", + "line": 6, + "end_line": 6, + "type": "action", + "arguments": [], + "doc": { + "description": "Comprehensive hook variants.", + "long_description": "", + "tags": [] + } + }, + { + "name": "action_with_args", + "line": 7, + "end_line": 7, + "type": "action", + "arguments": [ + "$a", + "$b" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "action_ref", + "line": 8, + "end_line": 8, + "type": "action_reference", + "arguments": [ + "array(&$x)" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "deprecated_action", + "line": 9, + "end_line": 9, + "type": "action_deprecated", + "arguments": [ + "array($y)", + "'5.0.0'", + "'new_action'" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "simple_filter", + "line": 11, + "end_line": 11, + "type": "filter", + "arguments": [ + "$value" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "filter_with_args", + "line": 12, + "end_line": 12, + "type": "filter", + "arguments": [ + "$value", + "$a", + "$b" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "filter_ref", + "line": 13, + "end_line": 13, + "type": "filter_reference", + "arguments": [ + "array($value, &$x)" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "deprecated_filter", + "line": 14, + "end_line": 14, + "type": "filter_deprecated", + "arguments": [ + "array($value)", + "'5.0.0'", + "'new_filter'" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "assigned_filter", + "line": 16, + "end_line": 16, + "type": "filter", + "arguments": [ + "$default" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "interpolated_{$dynamic}_action", + "line": 18, + "end_line": 18, + "type": "action", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "{$variable_hook}", + "line": 19, + "end_line": 19, + "type": "action", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "dynamic_{$suffix}", + "line": 20, + "end_line": 20, + "type": "action", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + } + ] + } +] diff --git a/tests/golden/snapshots/export__hooks.json b/tests/golden/snapshots/export__hooks.json new file mode 100644 index 00000000..c53ca048 --- /dev/null +++ b/tests/golden/snapshots/export__hooks.json @@ -0,0 +1,122 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "hooks.inc", + "root": "{{ROOT}}", + "uses": { + "functions": [ + { + "name": "do_action", + "line": 3, + "end_line": 3 + }, + { + "name": "do_action", + "line": 4, + "end_line": 4 + }, + { + "name": "do_action", + "line": 5, + "end_line": 5 + }, + { + "name": "do_action", + "line": 6, + "end_line": 6 + }, + { + "name": "do_action", + "line": 7, + "end_line": 7 + }, + { + "name": "apply_filters", + "line": 8, + "end_line": 8 + } + ] + }, + "hooks": [ + { + "name": "plain_action", + "line": 3, + "end_line": 3, + "type": "action", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "action_with_double_quotes", + "line": 4, + "end_line": 4, + "type": "action", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "{$variable}-action", + "line": 5, + "end_line": 5, + "type": "action", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "another-{$variable}-action", + "line": 6, + "end_line": 6, + "type": "action", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "hook_{$object->property}_pre", + "line": 7, + "end_line": 7, + "type": "action", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "plain_filter", + "line": 8, + "end_line": 8, + "type": "filter", + "arguments": [ + "$variable", + "$filter_context" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + } + ] + } +] diff --git a/tests/golden/snapshots/export__namespace.json b/tests/golden/snapshots/export__namespace.json new file mode 100644 index 00000000..9a95c6fb --- /dev/null +++ b/tests/golden/snapshots/export__namespace.json @@ -0,0 +1,27 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "namespace.inc", + "root": "{{ROOT}}", + "functions": [ + { + "name": "ohai", + "namespace": "Awesome\\Space", + "aliases": [], + "line": 5, + "end_line": 5, + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [] + } + ] + } +] diff --git a/tests/golden/snapshots/export__namespaced-uses.json b/tests/golden/snapshots/export__namespaced-uses.json new file mode 100644 index 00000000..a42e12c9 --- /dev/null +++ b/tests/golden/snapshots/export__namespaced-uses.json @@ -0,0 +1,164 @@ +[ + { + "file": { + "description": "Namespaced name-resolution fixture.", + "long_description": "", + "tags": [] + }, + "path": "namespaced-uses.inc", + "root": "{{ROOT}}", + "functions": [ + { + "name": "namespaced_func", + "namespace": "My\\Plugin", + "aliases": { + "Thing": "\\Other\\Thing", + "Aliased": "\\Some\\Long\\ClassName", + "helper": "\\Other\\helper" + }, + "line": 30, + "end_line": 32, + "arguments": [ + { + "name": "$t", + "default": null, + "type": "\\Other\\Thing" + } + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [], + "uses": { + "methods": [ + { + "name": "create", + "class": "\\Some\\Long\\ClassName", + "static": true, + "line": 31, + "end_line": 31 + } + ] + } + } + ], + "classes": [ + { + "name": "Widget", + "namespace": "My\\Plugin", + "line": 12, + "end_line": 28, + "final": false, + "abstract": false, + "extends": "\\WP_Widget", + "implements": [ + "\\Other\\Thing" + ], + "properties": [], + "methods": [ + { + "name": "render", + "namespace": "My\\Plugin", + "aliases": { + "Thing": "\\Other\\Thing", + "Aliased": "\\Some\\Long\\ClassName", + "helper": "\\Other\\helper" + }, + "line": 19, + "end_line": 27, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [ + { + "name": "$t", + "default": null, + "type": "\\Other\\Thing" + } + ], + "doc": { + "description": "Render.", + "long_description": "", + "tags": [ + { + "name": "param", + "content": "A thing.", + "types": [ + "\\Other\\Thing" + ], + "variable": "$t" + }, + { + "name": "return", + "content": "", + "types": [ + "\\Some\\Long\\ClassName" + ] + } + ] + }, + "uses": { + "methods": [ + { + "name": "__construct", + "class": "\\Some\\Long\\ClassName", + "static": false, + "line": 20, + "end_line": 20 + }, + { + "name": "make", + "class": "\\Other\\Thing", + "static": true, + "line": 21, + "end_line": 21 + }, + { + "name": "helper", + "class": "\\My\\Plugin\\Widget", + "static": true, + "line": 22, + "end_line": 22 + }, + { + "name": "widget", + "class": "\\WP_Widget", + "static": true, + "line": 23, + "end_line": 23 + }, + { + "name": "__construct", + "class": "\\DateTime", + "static": false, + "line": 26, + "end_line": 26 + } + ], + "functions": [ + { + "name": "\\do_action", + "line": 24, + "end_line": 24 + }, + { + "name": "\\Other\\helper", + "line": 25, + "end_line": 25 + } + ] + } + } + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + } + ] + } +] diff --git a/tests/golden/snapshots/export__type-tags.json b/tests/golden/snapshots/export__type-tags.json new file mode 100644 index 00000000..bb496c82 --- /dev/null +++ b/tests/golden/snapshots/export__type-tags.json @@ -0,0 +1,296 @@ +[ + { + "file": { + "description": "Test file for @type tag handling.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "1.0.0" + } + ] + }, + "path": "type-tags.inc", + "root": "{{ROOT}}", + "functions": [ + { + "name": "function_with_hash_notation", + "namespace": "global", + "aliases": [], + "line": 23, + "end_line": 25, + "arguments": [ + { + "name": "$args", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Function with hash notation in @param.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "1.0.0" + }, + { + "name": "param", + "content": "{ Optional. Array of arguments.
@type bool $enabled Whether the feature is enabled. Default false.
@type string $label The label to display.
@type int $max_items Maximum number of items. Default 10.
}", + "types": [ + "array" + ], + "variable": "$args" + }, + { + "name": "return", + "content": "True on success.", + "types": [ + "bool" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "function_with_multiple_hash_params", + "namespace": "global", + "aliases": [], + "line": 45, + "end_line": 46, + "arguments": [ + { + "name": "$options", + "default": null, + "type": "" + }, + { + "name": "$settings", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Function with multiple hash notation params.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.0.0" + }, + { + "name": "param", + "content": "{ Configuration options.
@type string $name The name.
@type int $count The count.
}", + "types": [ + "array" + ], + "variable": "$options" + }, + { + "name": "param", + "content": "{ Additional settings.
@type bool $active Whether active.
}", + "types": [ + "array" + ], + "variable": "$settings" + }, + { + "name": "return", + "content": "", + "types": [ + "void" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "function_with_simple_params", + "namespace": "global", + "aliases": [], + "line": 57, + "end_line": 59, + "arguments": [ + { + "name": "$name", + "default": null, + "type": "" + }, + { + "name": "$count", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Function with simple params (no hash notation).", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "1.0.0" + }, + { + "name": "param", + "content": "The name.", + "types": [ + "string" + ], + "variable": "$name" + }, + { + "name": "param", + "content": "The count.", + "types": [ + "int" + ], + "variable": "$count" + }, + { + "name": "return", + "content": "Success.", + "types": [ + "bool" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "function_with_return_hash", + "namespace": "global", + "aliases": [], + "line": 74, + "end_line": 76, + "arguments": [], + "doc": { + "description": "Function with hash notation in @return.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "1.0.0" + }, + { + "name": "return", + "content": "{ Result data.
@type int[] $updated An array of updated IDs.
@type int[] $skipped An array of skipped IDs.
@type string[] $errors An array of error messages.
}", + "types": [ + "array" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "function_with_param_and_return_hash", + "namespace": "global", + "aliases": [], + "line": 96, + "end_line": 98, + "arguments": [ + { + "name": "$options", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Function with hash notation in both @param and @return.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "1.0.0" + }, + { + "name": "param", + "content": "{ Input options.
@type string $mode The processing mode.
@type bool $force Whether to force the operation.
}", + "types": [ + "array" + ], + "variable": "$options" + }, + { + "name": "return", + "content": "{ Output data.
@type bool $success Whether the operation succeeded.
@type string $message A status message.
}", + "types": [ + "array" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "function_with_mixed_params", + "namespace": "global", + "aliases": [], + "line": 115, + "end_line": 117, + "arguments": [ + { + "name": "$name", + "default": null, + "type": "" + }, + { + "name": "$config", + "default": null, + "type": "" + }, + { + "name": "$limit", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Function with mixed params: some with hash, some without.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "1.0.0" + }, + { + "name": "param", + "content": "Simple string param.", + "types": [ + "string" + ], + "variable": "$name" + }, + { + "name": "param", + "content": "{ Configuration array.
@type int $timeout Timeout in seconds.
@type bool $retry Whether to retry on failure.
}", + "types": [ + "array" + ], + "variable": "$config" + }, + { + "name": "param", + "content": "Simple int param.", + "types": [ + "int" + ], + "variable": "$limit" + }, + { + "name": "return", + "content": "Success.", + "types": [ + "bool" + ] + } + ] + }, + "hooks": [] + } + ] + } +] diff --git a/tests/golden/snapshots/export__uses__constructor.json b/tests/golden/snapshots/export__uses__constructor.json new file mode 100644 index 00000000..2eabf4ee --- /dev/null +++ b/tests/golden/snapshots/export__uses__constructor.json @@ -0,0 +1,132 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "constructor.inc", + "root": "{{ROOT}}", + "uses": { + "methods": [ + { + "name": "__construct", + "class": "\\WP_Query", + "static": false, + "line": 3, + "end_line": 3 + }, + { + "name": "__construct", + "class": "$class", + "static": false, + "line": 20, + "end_line": 20 + } + ] + }, + "functions": [ + { + "name": "test", + "namespace": "global", + "aliases": [], + "line": 5, + "end_line": 7, + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [], + "uses": { + "methods": [ + { + "name": "__construct", + "class": "\\My_Class", + "static": false, + "line": 6, + "end_line": 6 + } + ] + } + } + ], + "classes": [ + { + "name": "My_Class", + "namespace": "global", + "line": 9, + "end_line": 18, + "final": false, + "abstract": false, + "extends": "\\Parent_Class", + "implements": [], + "properties": [], + "methods": [ + { + "name": "instance", + "namespace": "", + "aliases": [], + "line": 11, + "end_line": 13, + "final": false, + "abstract": false, + "static": true, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "uses": { + "methods": [ + { + "name": "__construct", + "class": "\\My_Class", + "static": false, + "line": 12, + "end_line": 12 + } + ] + } + }, + { + "name": "parent", + "namespace": "", + "aliases": [], + "line": 15, + "end_line": 17, + "final": false, + "abstract": false, + "static": true, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "uses": { + "methods": [ + { + "name": "__construct", + "class": "\\Parent_Class", + "static": false, + "line": 16, + "end_line": 16 + } + ] + } + } + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + } + ] + } +] diff --git a/tests/golden/snapshots/export__uses__methods.json b/tests/golden/snapshots/export__uses__methods.json new file mode 100644 index 00000000..39c8895c --- /dev/null +++ b/tests/golden/snapshots/export__uses__methods.json @@ -0,0 +1,139 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "methods.inc", + "root": "{{ROOT}}", + "uses": { + "methods": [ + { + "name": "static_method", + "class": "\\My_Class", + "static": true, + "line": 3, + "end_line": 3 + }, + { + "name": "update", + "class": "$wpdb", + "static": false, + "line": 5, + "end_line": 5 + } + ] + }, + "functions": [ + { + "name": "test", + "namespace": "global", + "aliases": [], + "line": 7, + "end_line": 11, + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [], + "uses": { + "methods": [ + { + "name": "another_method", + "class": "\\Another_Class", + "static": true, + "line": 8, + "end_line": 8 + }, + { + "name": "call_method", + "class": "get_class()", + "static": false, + "line": 10, + "end_line": 10 + } + ], + "functions": [ + { + "name": "get_class", + "line": 10, + "end_line": 10 + } + ] + } + } + ], + "classes": [ + { + "name": "My_Class", + "namespace": "global", + "line": 13, + "end_line": 21, + "final": false, + "abstract": false, + "extends": "\\Parent_Class", + "implements": [], + "properties": [], + "methods": [ + { + "name": "static_method", + "namespace": "", + "aliases": [], + "line": 15, + "end_line": 20, + "final": false, + "abstract": false, + "static": true, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "uses": { + "methods": [ + { + "name": "do_static_stuff", + "class": "\\Another_Class", + "static": true, + "line": 16, + "end_line": 16 + }, + { + "name": "do_stuff", + "class": "\\My_Class", + "static": true, + "line": 17, + "end_line": 17 + }, + { + "name": "go", + "class": "\\My_Class", + "static": false, + "line": 18, + "end_line": 18 + }, + { + "name": "do_parental_stuff", + "class": "\\Parent_Class", + "static": true, + "line": 19, + "end_line": 19 + } + ] + } + } + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + } + ] + } +] diff --git a/tests/golden/snapshots/export__uses__nested.json b/tests/golden/snapshots/export__uses__nested.json new file mode 100644 index 00000000..692a03bf --- /dev/null +++ b/tests/golden/snapshots/export__uses__nested.json @@ -0,0 +1,169 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "nested.inc", + "root": "{{ROOT}}", + "functions": [ + { + "name": "sub_test", + "namespace": "global", + "aliases": [], + "line": 7, + "end_line": 12, + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [], + "uses": { + "functions": [ + { + "name": "b_function", + "line": 9, + "end_line": 9 + } + ], + "methods": [ + { + "name": "static_method", + "class": "\\My_Class", + "static": true, + "line": 11, + "end_line": 11 + } + ] + } + }, + { + "name": "test", + "namespace": "global", + "aliases": [], + "line": 3, + "end_line": 17, + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [], + "uses": { + "functions": [ + { + "name": "a_function", + "line": 5, + "end_line": 5 + }, + { + "name": "sub_test", + "line": 14, + "end_line": 14 + } + ], + "methods": [ + { + "name": "do_things", + "class": "\\My_Class", + "static": true, + "line": 16, + "end_line": 16 + } + ] + } + }, + { + "name": "sub_method_test", + "namespace": "global", + "aliases": [], + "line": 25, + "end_line": 30, + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [], + "uses": { + "functions": [ + { + "name": "b_function", + "line": 27, + "end_line": 27 + } + ], + "methods": [ + { + "name": "a_method", + "class": "\\My_Class", + "static": true, + "line": 29, + "end_line": 29 + } + ] + } + } + ], + "classes": [ + { + "name": "My_Class", + "namespace": "global", + "line": 19, + "end_line": 34, + "final": false, + "abstract": false, + "extends": "\\Parent_Class", + "implements": [], + "properties": [], + "methods": [ + { + "name": "a_method", + "namespace": "", + "aliases": [], + "line": 21, + "end_line": 33, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "uses": { + "methods": [ + { + "name": "do_it", + "class": "\\My_Class", + "static": false, + "line": 23, + "end_line": 23 + } + ], + "functions": [ + { + "name": "do_things", + "line": 32, + "end_line": 32 + } + ] + } + } + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + } + ] + } +] diff --git a/tests/golden/snapshots/import__file.json b/tests/golden/snapshots/import__file.json new file mode 100644 index 00000000..9d6f3696 --- /dev/null +++ b/tests/golden/snapshots/import__file.json @@ -0,0 +1,75 @@ +[ + { + "file": { + "description": "File summary.", + "long_description": "

This is the longer file description. It can be very long, and even span multiple lines, if hard wrapped as it should be.

", + "tags": [ + { + "name": "package", + "content": "Something" + }, + { + "name": "since", + "content": "1.0.0" + } + ] + }, + "path": "file.inc", + "root": "{{ROOT}}", + "functions": [ + { + "name": "wp_parser_test_func", + "namespace": "global", + "aliases": [], + "line": 25, + "end_line": 28, + "arguments": [ + { + "name": "$var", + "default": null, + "type": "" + }, + { + "name": "$ids", + "default": "array()", + "type": "array" + } + ], + "doc": { + "description": "This is a function summary.", + "long_description": "

This function is just here for tests. This is its longer description.

", + "tags": [ + { + "name": "since", + "content": "1.4.0" + }, + { + "name": "param", + "content": "A string variable which is the first parameter.", + "types": [ + "string" + ], + "variable": "$var" + }, + { + "name": "param", + "content": "An array of user IDs.", + "types": [ + "int[]" + ], + "variable": "$ids" + }, + { + "name": "return", + "content": "The return type is random. (Not really.)", + "types": [ + "mixed" + ] + } + ] + }, + "hooks": [] + } + ] + } +] diff --git a/tests/golden/snapshots/source__actions.json b/tests/golden/snapshots/source__actions.json new file mode 100644 index 00000000..03db0c8a --- /dev/null +++ b/tests/golden/snapshots/source__actions.json @@ -0,0 +1,486 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "actions.php", + "root": "{{ROOT}}", + "uses": { + "functions": [ + { + "name": "do_action", + "line": 15, + "end_line": 15 + }, + { + "name": "do_action", + "line": 29, + "end_line": 29 + }, + { + "name": "do_action", + "line": 43, + "end_line": 43 + }, + { + "name": "do_action", + "line": 55, + "end_line": 55 + }, + { + "name": "do_action", + "line": 67, + "end_line": 67 + }, + { + "name": "do_action", + "line": 79, + "end_line": 79 + }, + { + "name": "do_action", + "line": 92, + "end_line": 92 + }, + { + "name": "do_action", + "line": 105, + "end_line": 105 + }, + { + "name": "do_action", + "line": 118, + "end_line": 118 + }, + { + "name": "do_action", + "line": 120, + "end_line": 120 + }, + { + "name": "do_action", + "line": 121, + "end_line": 121 + }, + { + "name": "do_action", + "line": 122, + "end_line": 122 + } + ] + }, + "hooks": [ + { + "name": "good_doc_static_action", + "line": 15, + "end_line": 15, + "type": "action", + "arguments": [ + "$option", + "$old_value", + "$value" + ], + "doc": { + "description": "This is a well documented action.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.9.0" + }, + { + "name": "param", + "content": "Name of the option to update.", + "types": [ + "string" + ], + "variable": "$option" + }, + { + "name": "param", + "content": "The old option value.", + "types": [ + "mixed" + ], + "variable": "$old_value" + }, + { + "name": "param", + "content": "The new option value.", + "types": [ + "mixed" + ], + "variable": "$value" + } + ] + } + }, + { + "name": "good_doc_dynamic_action_{$option}", + "line": 29, + "end_line": 29, + "type": "action", + "arguments": [ + "$old_value", + "$value" + ], + "doc": { + "description": "This is a well documented dynamic action.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.9.0" + }, + { + "name": "param", + "content": "Name of the option to update.", + "types": [ + "string" + ], + "variable": "$option" + }, + { + "name": "param", + "content": "The old option value.", + "types": [ + "mixed" + ], + "variable": "$old_value" + }, + { + "name": "param", + "content": "The new option value.", + "types": [ + "mixed" + ], + "variable": "$value" + } + ] + } + }, + { + "name": "good_doc_double_quotes_dynamic_action_{$option}", + "line": 43, + "end_line": 43, + "type": "action", + "arguments": [ + "$old_value", + "$value" + ], + "doc": { + "description": "This is a well documented dynamic action.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.9.0" + }, + { + "name": "param", + "content": "Name of the option to update.", + "types": [ + "string" + ], + "variable": "$option" + }, + { + "name": "param", + "content": "The old option value.", + "types": [ + "mixed" + ], + "variable": "$old_value" + }, + { + "name": "param", + "content": "The new option value.", + "types": [ + "mixed" + ], + "variable": "$value" + } + ] + } + }, + { + "name": "missing_since_static_action", + "line": 55, + "end_line": 55, + "type": "action", + "arguments": [ + "$option", + "$old_value", + "$value" + ], + "doc": { + "description": "This is an action missing the \"since\" line.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "param", + "content": "Name of the option to update.", + "types": [ + "string" + ], + "variable": "$option" + }, + { + "name": "param", + "content": "The old option value.", + "types": [ + "mixed" + ], + "variable": "$old_value" + }, + { + "name": "param", + "content": "The new option value.", + "types": [ + "mixed" + ], + "variable": "$value" + } + ] + } + }, + { + "name": "missing_since_dynamic_action_{$option}", + "line": 67, + "end_line": 67, + "type": "action", + "arguments": [ + "$old_value", + "$value" + ], + "doc": { + "description": "This is a dynamic action missing the \"since\" line.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "param", + "content": "Name of the option to update.", + "types": [ + "string" + ], + "variable": "$option" + }, + { + "name": "param", + "content": "The old option value.", + "types": [ + "mixed" + ], + "variable": "$old_value" + }, + { + "name": "param", + "content": "The new option value.", + "types": [ + "mixed" + ], + "variable": "$value" + } + ] + } + }, + { + "name": "missing_since_double_quotes_dynamic_action_{$option}", + "line": 79, + "end_line": 79, + "type": "action", + "arguments": [ + "$old_value", + "$value" + ], + "doc": { + "description": "This is a dynamic action missing the \"since\" line.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "param", + "content": "Name of the option to update.", + "types": [ + "string" + ], + "variable": "$option" + }, + { + "name": "param", + "content": "The old option value.", + "types": [ + "mixed" + ], + "variable": "$old_value" + }, + { + "name": "param", + "content": "The new option value.", + "types": [ + "mixed" + ], + "variable": "$value" + } + ] + } + }, + { + "name": "missing_param_static_action", + "line": 92, + "end_line": 92, + "type": "action", + "arguments": [ + "$option", + "$old_value", + "$value" + ], + "doc": { + "description": "This is an action missing a \"param\" line.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.9.0" + }, + { + "name": "param", + "content": "Name of the option to update.", + "types": [ + "string" + ], + "variable": "$option" + }, + { + "name": "param", + "content": "The new option value.", + "types": [ + "mixed" + ], + "variable": "$value" + } + ] + } + }, + { + "name": "missing_param_dynamic_action_{$option}", + "line": 105, + "end_line": 105, + "type": "action", + "arguments": [ + "$old_value", + "$value" + ], + "doc": { + "description": "This is a well documented dynamic action.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.9.0" + }, + { + "name": "param", + "content": "Name of the option to update.", + "types": [ + "string" + ], + "variable": "$option" + }, + { + "name": "param", + "content": "The new option value.", + "types": [ + "mixed" + ], + "variable": "$value" + } + ] + } + }, + { + "name": "missing_param_double_quotes_dynamic_action_{$option}", + "line": 118, + "end_line": 118, + "type": "action", + "arguments": [ + "$old_value", + "$value" + ], + "doc": { + "description": "This is a well documented dynamic action.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.9.0" + }, + { + "name": "param", + "content": "Name of the option to update.", + "types": [ + "string" + ], + "variable": "$option" + }, + { + "name": "param", + "content": "The new option value.", + "types": [ + "mixed" + ], + "variable": "$value" + } + ] + } + }, + { + "name": "no_doc_static_action", + "line": 120, + "end_line": 120, + "type": "action", + "arguments": [ + "$option", + "$old_value", + "$value" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "no_doc_dynamic_action_{$option}", + "line": 121, + "end_line": 121, + "type": "action", + "arguments": [ + "$old_value", + "$value" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "no_doc_double_quotes_dymanic_action_{$option}", + "line": 122, + "end_line": 122, + "type": "action", + "arguments": [ + "$old_value", + "$value" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + } + ] + } +] diff --git a/tests/golden/snapshots/source__bad-class-doc.json b/tests/golden/snapshots/source__bad-class-doc.json new file mode 100644 index 00000000..7db2bbbf --- /dev/null +++ b/tests/golden/snapshots/source__bad-class-doc.json @@ -0,0 +1,83 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "bad-class-doc.php", + "root": "{{ROOT}}", + "classes": [ + { + "name": "No_Since_Class", + "namespace": "global", + "line": 13, + "end_line": 15, + "final": false, + "abstract": false, + "extends": "", + "implements": [], + "properties": [], + "methods": [], + "doc": { + "description": "WordPress Error class.", + "long_description": "

Container for checking for WordPress errors and error messages. Return WP_Error and use {@link is_wp_error()} to check if this class is returned.
Many core WordPress functions pass this class in the event of an error and if not handled properly will result in code errors.

", + "tags": [ + { + "name": "package", + "content": "WordPress" + } + ] + } + }, + { + "name": "No_Package_Class", + "namespace": "global", + "line": 27, + "end_line": 29, + "final": false, + "abstract": false, + "extends": "", + "implements": [], + "properties": [], + "methods": [], + "doc": { + "description": "WordPress Error class.", + "long_description": "

Container for checking for WordPress errors and error messages. Return WP_Error and use {@link is_wp_error()} to check if this class is returned.
Many core WordPress functions pass this class in the event of an error and if not handled properly will result in code errors.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + } + ] + } + }, + { + "name": "No_Description_Class", + "namespace": "global", + "line": 35, + "end_line": 37, + "final": false, + "abstract": false, + "extends": "", + "implements": [], + "properties": [], + "methods": [], + "doc": { + "description": "", + "long_description": "", + "tags": [ + { + "name": "package", + "content": "WordPress" + }, + { + "name": "since", + "content": "2.1.0" + } + ] + } + } + ] + } +] diff --git a/tests/golden/snapshots/source__class-property-doc.json b/tests/golden/snapshots/source__class-property-doc.json new file mode 100644 index 00000000..4266843d --- /dev/null +++ b/tests/golden/snapshots/source__class-property-doc.json @@ -0,0 +1,308 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "class-property-doc.php", + "root": "{{ROOT}}", + "classes": [ + { + "name": "Bad_Property_Doc", + "namespace": "global", + "line": 14, + "end_line": 91, + "final": false, + "abstract": false, + "extends": "", + "implements": [], + "properties": [ + { + "name": "$private_good_doc_property", + "line": 23, + "end_line": 23, + "default": "'foo'", + "static": false, + "visibility": "private", + "doc": { + "description": "Stores the list of errors.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "var", + "content": "", + "types": [ + "array" + ], + "variable": "" + }, + { + "name": "access", + "content": "private" + } + ] + } + }, + { + "name": "$private_missing_description_property", + "line": 30, + "end_line": 30, + "default": "'string'", + "static": false, + "visibility": "private", + "doc": { + "description": "", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "var", + "content": "", + "types": [ + "array" + ], + "variable": "" + }, + { + "name": "access", + "content": "private" + } + ] + } + }, + { + "name": "$private_missing_since_property", + "line": 38, + "end_line": 38, + "default": "'foo'", + "static": false, + "visibility": "private", + "doc": { + "description": "Stores the list of errors.", + "long_description": "", + "tags": [ + { + "name": "var", + "content": "", + "types": [ + "array" + ], + "variable": "" + }, + { + "name": "access", + "content": "private" + } + ] + } + }, + { + "name": "$private_missing_var_property", + "line": 46, + "end_line": 46, + "default": "'foo'", + "static": false, + "visibility": "private", + "doc": { + "description": "Stores the list of errors.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "private" + } + ] + } + }, + { + "name": "$private_missing_access_property", + "line": 52, + "end_line": 52, + "default": "'string'", + "static": false, + "visibility": "private", + "doc": { + "description": "", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "var", + "content": "", + "types": [ + "array" + ], + "variable": "" + } + ] + } + }, + { + "name": "$public_good_doc_property", + "line": 61, + "end_line": 61, + "default": "'foo'", + "static": false, + "visibility": "public", + "doc": { + "description": "Stores the list of errors.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "var", + "content": "", + "types": [ + "array" + ], + "variable": "" + }, + { + "name": "access", + "content": "public" + } + ] + } + }, + { + "name": "$public_missing_description_property", + "line": 68, + "end_line": 68, + "default": "'string'", + "static": false, + "visibility": "public", + "doc": { + "description": "", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "var", + "content": "", + "types": [ + "array" + ], + "variable": "" + }, + { + "name": "access", + "content": "public" + } + ] + } + }, + { + "name": "$public_missing_since_property", + "line": 76, + "end_line": 76, + "default": "'foo'", + "static": false, + "visibility": "public", + "doc": { + "description": "Stores the list of errors.", + "long_description": "", + "tags": [ + { + "name": "var", + "content": "", + "types": [ + "array" + ], + "variable": "" + }, + { + "name": "access", + "content": "public" + } + ] + } + }, + { + "name": "$public_missing_var_property", + "line": 84, + "end_line": 84, + "default": "'foo'", + "static": false, + "visibility": "public", + "doc": { + "description": "Stores the list of errors.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "public" + } + ] + } + }, + { + "name": "$public_missing_access_property", + "line": 90, + "end_line": 90, + "default": "'string'", + "static": false, + "visibility": "public", + "doc": { + "description": "", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "var", + "content": "", + "types": [ + "array" + ], + "variable": "" + } + ] + } + } + ], + "methods": [], + "doc": { + "description": "WordPress Error class.", + "long_description": "

Container for checking for WordPress errors and error messages. Return WP_Error and use {@link is_wp_error()} to check if this class is returned.
Many core WordPress functions pass this class in the event of an error and if not handled properly will result in code errors.

", + "tags": [ + { + "name": "package", + "content": "WordPress" + }, + { + "name": "since", + "content": "2.1.0" + } + ] + } + } + ] + } +] diff --git a/tests/golden/snapshots/source__class_method_doc.json b/tests/golden/snapshots/source__class_method_doc.json new file mode 100644 index 00000000..87dbd52c --- /dev/null +++ b/tests/golden/snapshots/source__class_method_doc.json @@ -0,0 +1,1990 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "class_method_doc.php", + "root": "{{ROOT}}", + "classes": [ + { + "name": "Various_Method_Docs_Class", + "namespace": "global", + "line": 12, + "end_line": 478, + "final": false, + "abstract": false, + "extends": "", + "implements": [], + "properties": [], + "methods": [ + { + "name": "public_method_missing_description", + "namespace": "", + "aliases": [], + "line": 27, + "end_line": 29, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "public" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "public_method_missing_since", + "namespace": "", + "aliases": [], + "line": 47, + "end_line": 49, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "access", + "content": "public" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "public_method_missing_param", + "namespace": "", + "aliases": [], + "line": 65, + "end_line": 67, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "public" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "public_method_missing_return", + "namespace": "", + "aliases": [], + "line": 85, + "end_line": 87, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "public" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + } + ] + } + }, + { + "name": "public_method_missing_access", + "namespace": "", + "aliases": [], + "line": 105, + "end_line": 107, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "public_method_missing_see", + "namespace": "", + "aliases": [], + "line": 125, + "end_line": 127, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "public" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "public_method_missing_link", + "namespace": "", + "aliases": [], + "line": 145, + "end_line": 147, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "public" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "public_method_missing_global", + "namespace": "", + "aliases": [], + "line": 165, + "end_line": 167, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "public" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "protected_method_missing_description", + "namespace": "", + "aliases": [], + "line": 182, + "end_line": 184, + "final": false, + "abstract": false, + "static": false, + "visibility": "protected", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "protected" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "protected_method_missing_since", + "namespace": "", + "aliases": [], + "line": 202, + "end_line": 204, + "final": false, + "abstract": false, + "static": false, + "visibility": "protected", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "access", + "content": "protected" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "protected_method_missing_param", + "namespace": "", + "aliases": [], + "line": 220, + "end_line": 222, + "final": false, + "abstract": false, + "static": false, + "visibility": "protected", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "protected" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "protected_method_missing_return", + "namespace": "", + "aliases": [], + "line": 240, + "end_line": 242, + "final": false, + "abstract": false, + "static": false, + "visibility": "protected", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "protected" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + } + ] + } + }, + { + "name": "protected_method_missing_access", + "namespace": "", + "aliases": [], + "line": 260, + "end_line": 262, + "final": false, + "abstract": false, + "static": false, + "visibility": "protected", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "protected_method_missing_see", + "namespace": "", + "aliases": [], + "line": 280, + "end_line": 282, + "final": false, + "abstract": false, + "static": false, + "visibility": "protected", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "protected" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "protected_method_missing_link", + "namespace": "", + "aliases": [], + "line": 300, + "end_line": 302, + "final": false, + "abstract": false, + "static": false, + "visibility": "protected", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "protected" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "protected_method_missing_global", + "namespace": "", + "aliases": [], + "line": 320, + "end_line": 322, + "final": false, + "abstract": false, + "static": false, + "visibility": "protected", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "protected" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "private_method_missing_description", + "namespace": "", + "aliases": [], + "line": 337, + "end_line": 339, + "final": false, + "abstract": false, + "static": false, + "visibility": "private", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "private" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "private_method_missing_since", + "namespace": "", + "aliases": [], + "line": 357, + "end_line": 359, + "final": false, + "abstract": false, + "static": false, + "visibility": "private", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "access", + "content": "private" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "private_method_missing_param", + "namespace": "", + "aliases": [], + "line": 375, + "end_line": 377, + "final": false, + "abstract": false, + "static": false, + "visibility": "private", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "private" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "private_method_missing_return", + "namespace": "", + "aliases": [], + "line": 395, + "end_line": 397, + "final": false, + "abstract": false, + "static": false, + "visibility": "private", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "private" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + } + ] + } + }, + { + "name": "private_method_missing_access", + "namespace": "", + "aliases": [], + "line": 415, + "end_line": 417, + "final": false, + "abstract": false, + "static": false, + "visibility": "private", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "private_method_missing_see", + "namespace": "", + "aliases": [], + "line": 435, + "end_line": 437, + "final": false, + "abstract": false, + "static": false, + "visibility": "private", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "private" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "private_method_missing_link", + "namespace": "", + "aliases": [], + "line": 455, + "end_line": 457, + "final": false, + "abstract": false, + "static": false, + "visibility": "private", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "private" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "private_method_missing_global", + "namespace": "", + "aliases": [], + "line": 475, + "end_line": 477, + "final": false, + "abstract": false, + "static": false, + "visibility": "private", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "Short description.", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "access", + "content": "private" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + } + ], + "doc": { + "description": "This is a properly documented class.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "package", + "content": "WordPress" + }, + { + "name": "since", + "content": "2.1.0" + } + ] + } + } + ] + } +] diff --git a/tests/golden/snapshots/source__deprecated-file.json b/tests/golden/snapshots/source__deprecated-file.json new file mode 100644 index 00000000..51fd1ac6 --- /dev/null +++ b/tests/golden/snapshots/source__deprecated-file.json @@ -0,0 +1,112 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "deprecated-file.php", + "root": "{{ROOT}}", + "uses": { + "functions": [ + { + "name": "_deprecated_file", + "line": 2, + "end_line": 2, + "deprecation_version": "1.0" + }, + { + "name": "apply_filters", + "line": 23, + "end_line": 23 + }, + { + "name": "do_action", + "line": 28, + "end_line": 28 + } + ] + }, + "hooks": [ + { + "name": "deprecated_filter", + "line": 23, + "end_line": 23, + "type": "filter", + "arguments": [ + "$var" + ], + "doc": { + "description": "This filter should be marked as deprecated since 1.0", + "long_description": "", + "tags": [] + } + }, + { + "name": "deprecated_action", + "line": 28, + "end_line": 28, + "type": "action", + "arguments": [], + "doc": { + "description": "This action should be marked as deprecated since 1.0", + "long_description": "", + "tags": [] + } + } + ], + "functions": [ + { + "name": "should_be_deprecated", + "namespace": "global", + "aliases": [], + "line": 18, + "end_line": 18, + "arguments": [], + "doc": { + "description": "This function should be marked as deprecated since 1.0", + "long_description": "", + "tags": [] + }, + "hooks": [] + } + ], + "classes": [ + { + "name": "Should_Be_Deprectated", + "namespace": "global", + "line": 7, + "end_line": 13, + "final": false, + "abstract": false, + "extends": "", + "implements": [], + "properties": [], + "methods": [ + { + "name": "should_be_deprecated", + "namespace": "", + "aliases": [], + "line": 12, + "end_line": 12, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [], + "doc": { + "description": "This method should be marked as deprecated since 1.0", + "long_description": "", + "tags": [] + } + } + ], + "doc": { + "description": "This class should be marked as deprecated since 1.0", + "long_description": "", + "tags": [] + } + } + ] + } +] diff --git a/tests/golden/snapshots/source__filters.json b/tests/golden/snapshots/source__filters.json new file mode 100644 index 00000000..d00e817f --- /dev/null +++ b/tests/golden/snapshots/source__filters.json @@ -0,0 +1,477 @@ +[ + { + "file": { + "description": "This is a well documented filter.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "3.9.0" + }, + { + "name": "param", + "content": "Key/value pairs of strings.", + "types": [ + "array" + ], + "variable": "$mce_translation" + }, + { + "name": "param", + "content": "Locale.", + "types": [ + "string" + ], + "variable": "$mce_locale" + } + ] + }, + "path": "filters.php", + "root": "{{ROOT}}", + "uses": { + "functions": [ + { + "name": "apply_filters", + "line": 13, + "end_line": 13 + }, + { + "name": "apply_filters", + "line": 26, + "end_line": 26 + }, + { + "name": "apply_filters", + "line": 39, + "end_line": 39 + }, + { + "name": "apply_filters", + "line": 50, + "end_line": 50 + }, + { + "name": "apply_filters", + "line": 61, + "end_line": 61 + }, + { + "name": "apply_filters", + "line": 72, + "end_line": 72 + }, + { + "name": "apply_filters", + "line": 84, + "end_line": 84 + }, + { + "name": "apply_filters", + "line": 96, + "end_line": 96 + }, + { + "name": "apply_filters", + "line": 108, + "end_line": 108 + }, + { + "name": "apply_filters", + "line": 122, + "end_line": 122 + }, + { + "name": "apply_filters", + "line": 124, + "end_line": 124 + }, + { + "name": "apply_filters", + "line": 126, + "end_line": 126 + }, + { + "name": "apply_filters", + "line": 128, + "end_line": 128 + } + ] + }, + "hooks": [ + { + "name": "good_static_filter", + "line": 13, + "end_line": 13, + "type": "filter", + "arguments": [ + "$mce_translation", + "$mce_locale" + ], + "doc": { + "description": "This is a well documented filter.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "3.9.0" + }, + { + "name": "param", + "content": "Key/value pairs of strings.", + "types": [ + "array" + ], + "variable": "$mce_translation" + }, + { + "name": "param", + "content": "Locale.", + "types": [ + "string" + ], + "variable": "$mce_locale" + } + ] + } + }, + { + "name": "good_dynamic_filter_{$option}", + "line": 26, + "end_line": 26, + "type": "filter", + "arguments": [ + "$value", + "$old_value" + ], + "doc": { + "description": "This is a well documented dynamic filter.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.6.0" + }, + { + "name": "param", + "content": "The new, unserialized option value.", + "types": [ + "mixed" + ], + "variable": "$value" + }, + { + "name": "param", + "content": "The old option value.", + "types": [ + "mixed" + ], + "variable": "$old_value" + } + ] + } + }, + { + "name": "good_double_quotes_dynamic_filter_{$option}", + "line": 39, + "end_line": 39, + "type": "filter", + "arguments": [ + "$value", + "$old_value" + ], + "doc": { + "description": "This is a well documented dynamic filter.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.6.0" + }, + { + "name": "param", + "content": "The new, unserialized option value.", + "types": [ + "mixed" + ], + "variable": "$value" + }, + { + "name": "param", + "content": "The old option value.", + "types": [ + "mixed" + ], + "variable": "$old_value" + } + ] + } + }, + { + "name": "missing_since_static_filter", + "line": 50, + "end_line": 50, + "type": "filter", + "arguments": [ + "$mce_translation", + "$mce_locale" + ], + "doc": { + "description": "This is a filter missing the \"since\" line.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "param", + "content": "The new, unserialized option value.", + "types": [ + "mixed" + ], + "variable": "$value" + }, + { + "name": "param", + "content": "Locale.", + "types": [ + "string" + ], + "variable": "$mce_locale" + } + ] + } + }, + { + "name": "missing_since_dynamic_filter_{$option}", + "line": 61, + "end_line": 61, + "type": "filter", + "arguments": [ + "$value", + "$old_value" + ], + "doc": { + "description": "This is a dynamic filter missing the \"since\" line.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "param", + "content": "The new, unserialized option value.", + "types": [ + "mixed" + ], + "variable": "$value" + }, + { + "name": "param", + "content": "The old option value.", + "types": [ + "mixed" + ], + "variable": "$old_value" + } + ] + } + }, + { + "name": "missing_since_double_quotes_dynamic_filter_{$option}", + "line": 72, + "end_line": 72, + "type": "filter", + "arguments": [ + "$value", + "$old_value" + ], + "doc": { + "description": "This is a dynamic filter missing the \"since\" line.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "param", + "content": "The new, unserialized option value.", + "types": [ + "mixed" + ], + "variable": "$value" + }, + { + "name": "param", + "content": "The old option value.", + "types": [ + "mixed" + ], + "variable": "$old_value" + } + ] + } + }, + { + "name": "missing_param_static_filter", + "line": 84, + "end_line": 84, + "type": "filter", + "arguments": [ + "$mce_translation", + "$mce_locale" + ], + "doc": { + "description": "This is a filter missing one \"param\" line.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.6.0" + }, + { + "name": "param", + "content": "Locale.", + "types": [ + "string" + ], + "variable": "$mce_locale" + } + ] + } + }, + { + "name": "missing_param_dynamic_filter_{$option}", + "line": 96, + "end_line": 96, + "type": "filter", + "arguments": [ + "$value", + "$old_value" + ], + "doc": { + "description": "This is a dynamic filter missing one \"param\" line.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.6.0" + }, + { + "name": "param", + "content": "Locale.", + "types": [ + "string" + ], + "variable": "$mce_locale" + } + ] + } + }, + { + "name": "missing_param_double_quotes_dynamic_filter_{$option}", + "line": 108, + "end_line": 108, + "type": "filter", + "arguments": [ + "$value", + "$old_value" + ], + "doc": { + "description": "This is a dynamic filter missing one \"param\" line.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.6.0" + }, + { + "name": "param", + "content": "Locale.", + "types": [ + "string" + ], + "variable": "$mce_locale" + } + ] + } + }, + { + "name": "multiple_since_tags", + "line": 122, + "end_line": 122, + "type": "filter", + "arguments": [ + "$first_parameter", + "$second_parameter" + ], + "doc": { + "description": "This is a filter with multiple since tags", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "1.0" + }, + { + "name": "since", + "content": "1.9", + "description": "Added a new parameter to the filter" + }, + { + "name": "param", + "content": "", + "types": [ + "string" + ], + "variable": "$first_parameter" + }, + { + "name": "param", + "content": "", + "types": [ + "string" + ], + "variable": "$second_parameter" + } + ] + } + }, + { + "name": "no_doc_static_filter", + "line": 124, + "end_line": 124, + "type": "filter", + "arguments": [ + "$mce_translation", + "$mce_locale" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "no_doc_dynamic_filter_{$option}", + "line": 126, + "end_line": 126, + "type": "filter", + "arguments": [ + "$value", + "$old_value" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "no_doc_double_quotes_dynamic_filter_{$option}", + "line": 128, + "end_line": 128, + "type": "filter", + "arguments": [ + "$value", + "$old_value" + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + } + ] + } +] diff --git a/tests/golden/snapshots/source__functions.json b/tests/golden/snapshots/source__functions.json new file mode 100644 index 00000000..59326372 --- /dev/null +++ b/tests/golden/snapshots/source__functions.json @@ -0,0 +1,575 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "functions.php", + "root": "{{ROOT}}", + "functions": [ + { + "name": "good_doc_function", + "namespace": "global", + "aliases": [], + "line": 18, + "end_line": 20, + "arguments": [ + { + "name": "$thing", + "default": null, + "type": "" + }, + { + "name": "$var", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Short description. (use period)", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "3.9.0" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Description.", + "types": [ + "\\type" + ], + "variable": "$thing" + }, + { + "name": "param", + "content": "Optional. Description.", + "types": [ + "\\type" + ], + "variable": "$var" + }, + { + "name": "return", + "content": "Description.", + "types": [ + "\\type" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "missing_long_description_function", + "namespace": "global", + "aliases": [], + "line": 35, + "end_line": 37, + "arguments": [ + { + "name": "$thing", + "default": null, + "type": "" + }, + { + "name": "$var", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Short description. (use period)", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "3.9.0" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Description.", + "types": [ + "\\type" + ], + "variable": "$thing" + }, + { + "name": "param", + "content": "Optional. Description.", + "types": [ + "\\type" + ], + "variable": "$var" + }, + { + "name": "return", + "content": "Description.", + "types": [ + "\\type" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "missing_description_function", + "namespace": "global", + "aliases": [], + "line": 50, + "end_line": 52, + "arguments": [ + { + "name": "$thing", + "default": null, + "type": "" + }, + { + "name": "$var", + "default": null, + "type": "" + } + ], + "doc": { + "description": "", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "3.9.4" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Description.", + "types": [ + "\\type" + ], + "variable": "$thing" + }, + { + "name": "param", + "content": "Optional. Description.", + "types": [ + "\\type" + ], + "variable": "$var" + }, + { + "name": "return", + "content": "Description.", + "types": [ + "\\type" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "missing_since_function", + "namespace": "global", + "aliases": [], + "line": 67, + "end_line": 69, + "arguments": [ + { + "name": "$thing", + "default": null, + "type": "" + }, + { + "name": "$var", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Short description. (use period)", + "long_description": "

Long description.

", + "tags": [ + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Description.", + "types": [ + "\\type" + ], + "variable": "$thing" + }, + { + "name": "param", + "content": "Optional. Description.", + "types": [ + "\\type" + ], + "variable": "$var" + }, + { + "name": "return", + "content": "Description.", + "types": [ + "\\type" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "missing_see_function", + "namespace": "global", + "aliases": [], + "line": 85, + "end_line": 87, + "arguments": [ + { + "name": "$thing", + "default": null, + "type": "" + }, + { + "name": "$var", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Short description. (use period)", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "3.9.0" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Description.", + "types": [ + "\\type" + ], + "variable": "$thing" + }, + { + "name": "param", + "content": "Optional. Description.", + "types": [ + "\\type" + ], + "variable": "$var" + }, + { + "name": "return", + "content": "Description.", + "types": [ + "\\type" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "missing_link_function", + "namespace": "global", + "aliases": [], + "line": 103, + "end_line": 105, + "arguments": [ + { + "name": "$thing", + "default": null, + "type": "" + }, + { + "name": "$var", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Short description. (use period)", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "3.9.0" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Description.", + "types": [ + "\\type" + ], + "variable": "$thing" + }, + { + "name": "param", + "content": "Optional. Description.", + "types": [ + "\\type" + ], + "variable": "$var" + }, + { + "name": "return", + "content": "Description.", + "types": [ + "\\type" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "missing_global_function", + "namespace": "global", + "aliases": [], + "line": 121, + "end_line": 123, + "arguments": [ + { + "name": "$thing", + "default": null, + "type": "" + }, + { + "name": "$var", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Short description. (use period)", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "3.9.0" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "param", + "content": "Description.", + "types": [ + "\\type" + ], + "variable": "$thing" + }, + { + "name": "param", + "content": "Optional. Description.", + "types": [ + "\\type" + ], + "variable": "$var" + }, + { + "name": "return", + "content": "Description.", + "types": [ + "\\type" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "missing_param_function", + "namespace": "global", + "aliases": [], + "line": 138, + "end_line": 140, + "arguments": [ + { + "name": "$thing", + "default": null, + "type": "" + }, + { + "name": "$var", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Short description. (use period)", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "3.9.0" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "return", + "content": "Description.", + "types": [ + "\\type" + ] + } + ] + }, + "hooks": [] + }, + { + "name": "missing_return_function", + "namespace": "global", + "aliases": [], + "line": 156, + "end_line": 158, + "arguments": [ + { + "name": "$thing", + "default": null, + "type": "" + }, + { + "name": "$var", + "default": null, + "type": "" + } + ], + "doc": { + "description": "Short description. (use period)", + "long_description": "

Long description.

", + "tags": [ + { + "name": "since", + "content": "3.9.0" + }, + { + "name": "see", + "content": "relied on", + "refers": "Function/method/class" + }, + { + "name": "link", + "content": "URL", + "link": "URL" + }, + { + "name": "global", + "content": "type $varname Short description." + }, + { + "name": "param", + "content": "Description.", + "types": [ + "\\type" + ], + "variable": "$thing" + }, + { + "name": "param", + "content": "Optional. Description.", + "types": [ + "\\type" + ], + "variable": "$var" + } + ] + }, + "hooks": [] + } + ] + } +] diff --git a/tests/golden/snapshots/source__good-class.json b/tests/golden/snapshots/source__good-class.json new file mode 100644 index 00000000..32699a65 --- /dev/null +++ b/tests/golden/snapshots/source__good-class.json @@ -0,0 +1,341 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "good-class.php", + "root": "{{ROOT}}", + "classes": [ + { + "name": "Good_Doc_Class", + "namespace": "global", + "line": 12, + "end_line": 91, + "final": false, + "abstract": false, + "extends": "", + "implements": [], + "properties": [ + { + "name": "$good_doc_private_property_from_good_doc_class", + "line": 21, + "end_line": 21, + "default": "array()", + "static": false, + "visibility": "private", + "doc": { + "description": "This is a properly documented private property.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "var", + "content": "", + "types": [ + "array" + ], + "variable": "" + }, + { + "name": "access", + "content": "private" + } + ] + } + }, + { + "name": "$good_doc_protected_property_from_good_doc_class", + "line": 30, + "end_line": 30, + "default": "array()", + "static": false, + "visibility": "protected", + "doc": { + "description": "This is a properly documented protected property.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "var", + "content": "", + "types": [ + "array" + ], + "variable": "" + }, + { + "name": "access", + "content": "protected" + } + ] + } + }, + { + "name": "$good_doc_public_property_from_good_doc_class", + "line": 39, + "end_line": 39, + "default": "array()", + "static": false, + "visibility": "public", + "doc": { + "description": "This is a properly documented public property.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "var", + "content": "", + "types": [ + "array" + ], + "variable": "" + }, + { + "name": "access", + "content": "public" + } + ] + } + } + ], + "methods": [ + { + "name": "good_doc_public_method_from_good_doc_class", + "namespace": "", + "aliases": [], + "line": 54, + "end_line": 56, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "This is a properly documented public method.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "good_doc_protected_method_from_good_doc_class", + "namespace": "", + "aliases": [], + "line": 71, + "end_line": 73, + "final": false, + "abstract": false, + "static": false, + "visibility": "protected", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "This is a properly documented protected method.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + }, + { + "name": "good_doc_private_method_from_good_doc_class", + "namespace": "", + "aliases": [], + "line": 88, + "end_line": 90, + "final": false, + "abstract": false, + "static": false, + "visibility": "private", + "arguments": [ + { + "name": "$code", + "default": "''", + "type": "" + }, + { + "name": "$message", + "default": "''", + "type": "" + }, + { + "name": "$data", + "default": "''", + "type": "" + } + ], + "doc": { + "description": "This is a properly documented private method.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "since", + "content": "2.1.0" + }, + { + "name": "param", + "content": "Error code", + "types": [ + "string", + "int" + ], + "variable": "$code" + }, + { + "name": "param", + "content": "Error message", + "types": [ + "string" + ], + "variable": "$message" + }, + { + "name": "param", + "content": "Optional. Error data.", + "types": [ + "mixed" + ], + "variable": "$data" + }, + { + "name": "return", + "content": "", + "types": [ + "\\WP_Error" + ] + } + ] + } + } + ], + "doc": { + "description": "This is a properly documented class.", + "long_description": "

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

", + "tags": [ + { + "name": "package", + "content": "WordPress" + }, + { + "name": "since", + "content": "2.1.0" + } + ] + } + } + ] + } +] diff --git a/tests/golden/snapshots/source__relationships.json b/tests/golden/snapshots/source__relationships.json new file mode 100644 index 00000000..42457c07 --- /dev/null +++ b/tests/golden/snapshots/source__relationships.json @@ -0,0 +1,494 @@ +[ + { + "file": { + "description": "", + "long_description": "", + "tags": [] + }, + "path": "relationships.php", + "root": "{{ROOT}}", + "functions": [ + { + "name": "relate_function1", + "namespace": "global", + "aliases": [], + "line": 3, + "end_line": 5, + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [], + "uses": { + "functions": [ + { + "name": "relate_function2", + "line": 4, + "end_line": 4 + } + ] + } + }, + { + "name": "relate_function2", + "namespace": "global", + "aliases": [], + "line": 7, + "end_line": 15, + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [ + { + "name": "relate-hook", + "line": 12, + "end_line": 12, + "type": "filter", + "arguments": [ + "true" + ], + "doc": { + "description": "A relationship hook", + "long_description": "", + "tags": [] + } + } + ], + "uses": { + "functions": [ + { + "name": "apply_filters", + "line": 12, + "end_line": 12 + } + ] + } + }, + { + "name": "relate_function3", + "namespace": "global", + "aliases": [], + "line": 17, + "end_line": 19, + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [], + "uses": { + "methods": [ + { + "name": "relate_method1", + "class": "\\wpdb", + "static": true, + "line": 18, + "end_line": 18 + } + ] + } + }, + { + "name": "relate_function4", + "namespace": "global", + "aliases": [], + "line": 21, + "end_line": 25, + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [], + "uses": { + "methods": [ + { + "name": "__construct", + "class": "\\wpdb", + "static": false, + "line": 22, + "end_line": 22 + }, + { + "name": "relate_method4", + "class": "$wpdb", + "static": false, + "line": 24, + "end_line": 24 + } + ] + } + }, + { + "name": "relate_function5", + "namespace": "global", + "aliases": [], + "line": 27, + "end_line": 29, + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [], + "uses": { + "methods": [ + { + "name": "some_function", + "class": "\\wpdb::relate_method2()", + "static": false, + "line": 28, + "end_line": 28 + }, + { + "name": "relate_method2", + "class": "\\wpdb", + "static": true, + "line": 28, + "end_line": 28 + } + ] + } + }, + { + "name": "relate_function6", + "namespace": "global", + "aliases": [], + "line": 31, + "end_line": 33, + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "hooks": [], + "uses": { + "methods": [ + { + "name": "relate_method1", + "class": "wp_screen()", + "static": false, + "line": 32, + "end_line": 32 + } + ], + "functions": [ + { + "name": "wp_screen", + "line": 32, + "end_line": 32 + } + ] + } + } + ], + "classes": [ + { + "name": "wpdb", + "namespace": "global", + "line": 35, + "end_line": 76, + "final": false, + "abstract": false, + "extends": "", + "implements": [], + "properties": [], + "methods": [ + { + "name": "__construct", + "namespace": "", + "aliases": [], + "line": 37, + "end_line": 37, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "relate_method1", + "namespace": "", + "aliases": [], + "line": 39, + "end_line": 41, + "final": false, + "abstract": false, + "static": true, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "uses": { + "methods": [ + { + "name": "relate_method2", + "class": "\\wpdb", + "static": true, + "line": 40, + "end_line": 40 + } + ] + } + }, + { + "name": "relate_method2", + "namespace": "", + "aliases": [], + "line": 43, + "end_line": 53, + "final": false, + "abstract": false, + "static": true, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "uses": { + "functions": [ + { + "name": "apply_filters", + "line": 52, + "end_line": 52 + } + ] + }, + "hooks": [ + { + "name": "meh-hook", + "line": 52, + "end_line": 52, + "type": "filter", + "arguments": [ + "$meh" + ], + "doc": { + "description": "Filter a aCustomize setting value in un-slashed form.", + "long_description": "", + "tags": [ + { + "name": "since", + "content": "3.5.0" + }, + { + "name": "param", + "content": "Value of the setting.", + "types": [ + "mixed" + ], + "variable": "$value" + }, + { + "name": "param", + "content": "WP_Customize_Setting instance.", + "types": [ + "\\WP_Customize_Setting" + ], + "variable": "$this" + } + ] + } + } + ] + }, + { + "name": "relate_method3", + "namespace": "", + "aliases": [], + "line": 55, + "end_line": 57, + "final": false, + "abstract": false, + "static": true, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "uses": { + "functions": [ + { + "name": "relate_function1", + "line": 56, + "end_line": 56 + } + ] + } + }, + { + "name": "relate_method4", + "namespace": "", + "aliases": [], + "line": 59, + "end_line": 61, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "uses": { + "functions": [ + { + "name": "relate_function2", + "line": 60, + "end_line": 60 + } + ] + } + }, + { + "name": "relate_method5", + "namespace": "", + "aliases": [], + "line": 63, + "end_line": 65, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "uses": { + "methods": [ + { + "name": "relate_method4", + "class": "\\wpdb", + "static": false, + "line": 64, + "end_line": 64 + } + ] + } + }, + { + "name": "relate_method6", + "namespace": "", + "aliases": [], + "line": 67, + "end_line": 69, + "final": false, + "abstract": false, + "static": true, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "uses": { + "methods": [ + { + "name": "relate_method1", + "class": "\\wpdb", + "static": true, + "line": 68, + "end_line": 68 + } + ] + } + }, + { + "name": "relate_method7", + "namespace": "", + "aliases": [], + "line": 71, + "end_line": 75, + "final": false, + "abstract": false, + "static": true, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + }, + "uses": { + "methods": [ + { + "name": "relate_method5", + "class": "$wpdb", + "static": false, + "line": 74, + "end_line": 74 + } + ] + } + } + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + }, + { + "name": "WP_Screen", + "namespace": "global", + "line": 78, + "end_line": 80, + "final": false, + "abstract": false, + "extends": "", + "implements": [], + "properties": [], + "methods": [ + { + "name": "relate_method1", + "namespace": "", + "aliases": [], + "line": 79, + "end_line": 79, + "final": false, + "abstract": false, + "static": false, + "visibility": "public", + "arguments": [], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + } + ], + "doc": { + "description": "", + "long_description": "", + "tags": [] + } + } + ] + } +] diff --git a/tests/golden/test-golden-master.php b/tests/golden/test-golden-master.php new file mode 100644 index 00000000..4e8d71e8 --- /dev/null +++ b/tests/golden/test-golden-master.php @@ -0,0 +1,61 @@ +markTestSkipped( + "No golden snapshot for '{$slug}'. Generate it on the old stack with bin/generate-golden.php." + ); + } + + $expected = file_get_contents( $snapshot ); + $actual = \WP_Parser\Golden\to_json( \WP_Parser\Golden\parse_entry( $entry ) ); + + $this->assertSame( + $expected, + $actual, + "Parser output drifted from the golden master for '{$slug}'." + ); + } + + /** + * @return array + */ + public function corpus_provider() { + $cases = array(); + foreach ( \WP_Parser\Golden\corpus() as $slug => $entry ) { + $cases[ $slug ] = array( $slug, $entry ); + } + + return $cases; + } +} diff --git a/tests/phpunit/README.md b/tests/phpunit/README.md new file mode 100644 index 00000000..ce4abb0a --- /dev/null +++ b/tests/phpunit/README.md @@ -0,0 +1,56 @@ +# WordPress integration test suite + +These tests exercise the **WordPress side** of the plugin — the importer turning +parsed data into posts/taxonomies/meta, and the related templates — so they need +a real WordPress install. They complement the [golden-master parser +harness](../golden/README.md), which is WordPress-free and only covers +`parse_files()`. + +- `tests/phpunit/tests/export/**` — parser output assertions +- `tests/phpunit/tests/import/file.php` — end-to-end importer test (posts, terms, + `_wp-parser_*` meta) + +## Baseline status + +On the **old parser stack** (PHP 7.4, `phpdocumentor/reflection ~3.0`) the suite +is green: + +``` +PHPUnit 7.5.20 — OK (22 tests, 125 assertions) +``` + +This is the regression baseline. Every migration stage must keep it green (run on +PHP 7.4 until Stage 2 moves the floor to PHP 8.2 + PHPUnit 9). + +## Running locally (wp-env / Docker) + +Requires Docker and Node. The environment is defined in `.wp-env.json` (PHP 7.4, +plugin + Posts-to-Posts). + +```bash +npm install # installs @wordpress/env +npm run test:phpunit:setup # wp-env start + composer install (in the container) +npm run test:phpunit # runs phpunit -c phpunit.xml.dist in tests-wordpress +``` + +Or drive `@wordpress/env` directly without the npm scripts: + +```bash +npx @wordpress/env start +npx @wordpress/env run --env-cwd='wp-content/plugins/phpdoc-parser' tests-wordpress composer install +npx @wordpress/env run --env-cwd='wp-content/plugins/phpdoc-parser' tests-wordpress \ + vendor/phpunit/phpunit/phpunit -c phpunit.xml.dist +``` + +### Gotcha: GitHub HTTPS→SSH git rewrite + +`wp-env start` clones `WordPress/wordpress-develop` over **HTTPS**. If your global +git config rewrites GitHub HTTPS to SSH (e.g. +`url."git@github.com:".insteadOf "https://github.com/"`) and outbound SSH port 22 +is blocked, the clone fails with `Connection to ... port 22: Operation timed out`. + +Neutralize the rewrite for that one command — your config files stay untouched: + +```bash +GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null npx @wordpress/env start +``` diff --git a/tests/phpunit/includes/export-testcase.php b/tests/phpunit/includes/export-testcase.php index d74bf9f4..88a061fd 100644 --- a/tests/phpunit/includes/export-testcase.php +++ b/tests/phpunit/includes/export-testcase.php @@ -134,7 +134,7 @@ protected function assertFunctionUses( $type, $function_name, $entity ) { , $function_name ); - $this->assertInternalType( 'array', $function_data ); + $this->assertIsArray( $function_data ); $this->assertEntityUses( $function_data, $type, $entity ); } @@ -153,7 +153,7 @@ protected function assertFunctionNotUses( $type, $function_name, $entity ) { , $function_name ); - $this->assertInternalType( 'array', $function_data ); + $this->assertIsArray( $function_data ); $this->assertEntityNotUses( $function_data, $type, $entity ); } @@ -173,7 +173,7 @@ protected function assertMethodUses( $type, $class_name, $method_name, $entity ) , $class_name ); - $this->assertInternalType( 'array', $class_data ); + $this->assertIsArray( $class_data ); $method_data = $this->find_entity_data_in( $class_data @@ -181,7 +181,7 @@ protected function assertMethodUses( $type, $class_name, $method_name, $entity ) , $method_name ); - $this->assertInternalType( 'array', $method_data ); + $this->assertIsArray( $method_data ); $this->assertEntityUses( $method_data, $type, $entity ); } @@ -201,7 +201,7 @@ protected function assertMethodNotUses( $type, $class_name, $method_name, $entit , $class_name ); - $this->assertInternalType( 'array', $class_data ); + $this->assertIsArray( $class_data ); $method_data = $this->find_entity_data_in( $class_data @@ -209,7 +209,7 @@ protected function assertMethodNotUses( $type, $class_name, $method_name, $entit , $method_name ); - $this->assertInternalType( 'array', $method_data ); + $this->assertIsArray( $method_data ); $this->assertEntityNotUses( $method_data, $type, $entity ); } @@ -405,7 +405,7 @@ protected function assertClassHasDocs( $class, $docs ) { protected function assertMethodHasDocs( $class, $method, $docs ) { $class = $this->find_entity_data_in( $this->export_data, 'classes', $class ); - $this->assertInternalType( 'array', $class ); + $this->assertIsArray( $class ); $method = $this->find_entity_data_in( $class, 'methods', $method ); $this->assertEntityHasDocs( $method, $docs ); @@ -421,7 +421,7 @@ protected function assertMethodHasDocs( $class, $method, $docs ) { protected function assertPropertyHasDocs( $class, $property, $docs ) { $class = $this->find_entity_data_in( $this->export_data, 'classes', $class ); - $this->assertInternalType( 'array', $class ); + $this->assertIsArray( $class ); $property = $this->find_entity_data_in( $class, 'properties', $property ); $this->assertEntityHasDocs( $property, $docs ); diff --git a/tests/phpunit/tests/export/class-features.inc b/tests/phpunit/tests/export/class-features.inc new file mode 100644 index 00000000..4c2819eb --- /dev/null +++ b/tests/phpunit/tests/export/class-features.inc @@ -0,0 +1,36 @@ +method(); +} ); + +$callback = function ( $arg ) use ( $x ) { + return helper( $arg ); +}; + +function with_closure() { + $fn = function () { + inner_call(); + }; + + return $fn; +} diff --git a/tests/phpunit/tests/export/constants-includes.inc b/tests/phpunit/tests/export/constants-includes.inc new file mode 100644 index 00000000..932f12bf --- /dev/null +++ b/tests/phpunit/tests/export/constants-includes.inc @@ -0,0 +1,29 @@ + Quoted Content. + * > On multiple lines. + * + * ## Inline Formatting includes Headings. + * + * There's also _italics_ and **bold**. + */ +function test_markdown_in_description() { + +} \ No newline at end of file diff --git a/tests/phpunit/tests/export/hooks-extra.inc b/tests/phpunit/tests/export/hooks-extra.inc new file mode 100644 index 00000000..be6f6640 --- /dev/null +++ b/tests/phpunit/tests/export/hooks-extra.inc @@ -0,0 +1,20 @@ +doc( "/**\n * Summary here.\n *\n * Long\n * description.\n */" ); + + $this->assertSame( 'Summary here.', $db->getShortDescription() ); + $this->assertStringContainsString( '

', $db->getLongDescription()->getFormattedContents() ); + } + + public function test_param_return_since_tags() { + $db = $this->doc( + "/**\n * S.\n *\n * @since 1.2.0\n * @param string \$var A value.\n" + . " * @param WP_Post \$post Post object.\n * @return bool Result.\n */" + ); + + $tags = $db->getTags(); + + // @since exposes a version but no types (so export_docblock keeps it a version tag). + $this->assertSame( 'since', $tags[0]->getName() ); + $this->assertSame( '1.2.0', $tags[0]->getVersion() ); + $this->assertFalse( method_exists( $tags[0], 'getTypes' ) ); + + // @param: types + variable. + $this->assertSame( 'param', $tags[1]->getName() ); + $this->assertSame( array( 'string' ), $tags[1]->getTypes() ); + $this->assertSame( '$var', $tags[1]->getVariableName() ); + + // Class types resolve to a leading-backslash FQN. + $this->assertSame( array( '\\WP_Post' ), $tags[2]->getTypes() ); + + // @return: types, no variable. + $this->assertSame( array( 'bool' ), $tags[3]->getTypes() ); + $this->assertFalse( method_exists( $tags[3], 'getVariableName' ) ); + } + + public function test_see_and_link_reconstruction() { + $db = $this->doc( "/**\n * S.\n *\n * @see Function/method/class relied on\n * @link URL\n */" ); + + $tags = $db->getTags(); + + // @see is invalid to reflection-docblock (non-FQSEN); reconstructed loosely. + $this->assertSame( 'see', $tags[0]->getName() ); + $this->assertSame( 'Function/method/class', $tags[0]->getReference() ); + $this->assertSame( 'relied on', $tags[0]->getDescription() ); + + // @link with no description uses the URL as content. + $this->assertSame( 'link', $tags[1]->getName() ); + $this->assertSame( 'URL', $tags[1]->getLink() ); + $this->assertSame( 'URL', $tags[1]->getDescription() ); + } + + /** + * A @see target reflection-docblock recognizes as an FQSEN comes back normalized + * with a leading backslash; the legacy parser kept it as written, so the adapter + * strips the single normalization backslash. (Golden: tests/.../export/docblocks.) + */ + public function test_see_fqsen_reference_drops_normalization_backslash() { + $db = $this->doc( "/**\n * S.\n *\n * @see self::other_method() The description.\n */" ); + + $tags = $db->getTags(); + + $this->assertSame( 'see', $tags[0]->getName() ); + $this->assertSame( 'self::other_method()', $tags[0]->getReference() ); + $this->assertSame( 'The description.', $tags[0]->getDescription() ); + } + + /** + * Modern @param type syntaxes the legacy parser mangled (a leading "?" nullable + * and parenthesized unions) — reflection-docblock 6 parses them correctly. The + * old parser produced garbage for these, so they are excluded from the byte-for- + * byte golden suite and locked here instead. Class names still resolve to the + * leading-backslash FQN form the legacy output used. + */ + public function test_modern_param_type_syntax() { + $db = $this->doc( + "/**\n * S.\n *\n" + . " * @param ?string \$nullable_string A nullable string.\n" + . " * @param ( WP_Post | null ) \$nullable_post A nullable post.\n */" + ); + + $tags = $db->getTags(); + + $this->assertSame( 'param', $tags[0]->getName() ); + $this->assertSame( array( '?string' ), $tags[0]->getTypes() ); + $this->assertSame( '$nullable_string', $tags[0]->getVariableName() ); + + $this->assertSame( 'param', $tags[1]->getName() ); + $this->assertSame( array( '\\WP_Post', 'null' ), $tags[1]->getTypes() ); + $this->assertSame( '$nullable_post', $tags[1]->getVariableName() ); + } +} diff --git a/tests/unit/file-reflector-test.php b/tests/unit/file-reflector-test.php new file mode 100644 index 00000000..f5b62ce0 --- /dev/null +++ b/tests/unit/file-reflector-test.php @@ -0,0 +1,206 @@ +setFilename( basename( $path ) ); + $file->process(); + + return $file; + } + + public function test_extracts_namespaced_function() { + $functions = $this->reflect( 'phpunit/tests/export/namespace.inc' )->getFunctions(); + + $this->assertCount( 1, $functions ); + $this->assertSame( 'ohai', $functions[0]->getShortName() ); + $this->assertSame( 'Awesome\\Space', $functions[0]->getNamespace() ); + $this->assertSame( array(), $functions[0]->getNamespaceAliases() ); + $this->assertSame( 5, $functions[0]->getLineNumber() ); + $this->assertSame( array(), $functions[0]->getArguments() ); + } + + public function test_nested_functions_ordered_inner_first_and_extends_resolved() { + $file = $this->reflect( 'phpunit/tests/export/uses/nested.inc' ); + + $names = array_map( + static function ( $fn ) { + return $fn->getShortName(); + }, + $file->getFunctions() + ); + // Inner functions complete first, matching the legacy parser's order. + $this->assertSame( array( 'sub_test', 'test', 'sub_method_test' ), $names ); + + $classes = $file->getClasses(); + $this->assertCount( 1, $classes ); + $this->assertSame( 'My_Class', $classes[0]->getShortName() ); + $this->assertSame( '\\Parent_Class', $classes[0]->getParentClass() ); + } + + public function test_extracts_class_members() { + $class = $this->reflect( 'source/good-class.php' )->getClasses()[0]; + + $this->assertSame( 'Good_Doc_Class', $class->getShortName() ); + $this->assertSame( 'global', $class->getNamespace() ); + $this->assertFalse( $class->isAbstract() ); + $this->assertFalse( $class->isFinal() ); + $this->assertSame( '', $class->getParentClass() ); + + $property = $class->getProperties()[0]; + $this->assertSame( '$good_doc_private_property_from_good_doc_class', $property->getName() ); + $this->assertSame( 'private', $property->getVisibility() ); + $this->assertFalse( $property->isStatic() ); + $this->assertSame( 'array()', $property->getDefault() ); + + $method = $class->getMethods()[0]; + $this->assertSame( '', $method->getNamespace() ); + $this->assertSame( 'public', $method->getVisibility() ); + $arguments = $method->getArguments(); + $this->assertSame( '$code', $arguments[0]->getName() ); + $this->assertSame( "''", $arguments[0]->getDefault() ); + $this->assertSame( '', $arguments[0]->getType() ); + } + + public function test_handles_anonymous_class_without_fatal() { + // Real WordPress core instantiates anonymous classes; this must not fatal. + $tmp = tempnam( sys_get_temp_dir(), 'wpp' ); + file_put_contents( + $tmp, + "setFilename( 'anon.php' ); + $file->process(); + } finally { + unlink( $tmp ); + } + + $functions = $file->getFunctions(); + $this->assertCount( 1, $functions ); + + $uses = $functions[0]->uses; + $this->assertNotEmpty( $uses['methods'] ); + + $name = $uses['methods'][0]->getName(); + $this->assertSame( '', $name[0] ); // Anonymous class — no name. + $this->assertSame( '__construct', $name[1] ); + } + + public function test_handles_modern_typehints() { + // The legacy php-parser v1 fataled on nullable typehints, so there is no + // golden oracle for these — reproducing them is the migration's whole point. + // Nullable and union typehints (and return types) must parse, and class names + // resolve to the leading-backslash FQN form the legacy output used elsewhere. + $tmp = tempnam( sys_get_temp_dir(), 'wpp' ); + file_put_contents( + $tmp, + "setFilename( 'modern.php' ); + $file->process(); + } finally { + unlink( $tmp ); + } + + $arguments = $file->getFunctions()[0]->getArguments(); + + $this->assertSame( '$post', $arguments[0]->getName() ); + $this->assertSame( '?\\WP_Post', $arguments[0]->getType() ); + $this->assertSame( '$id', $arguments[1]->getName() ); + $this->assertSame( 'int|string', $arguments[1]->getType() ); + } + + public function test_file_docblock_claimed_by_first_statement() { + // A docblock attached to the open tag (line <= 2) floats up to the file only + // when the first statement does not claim it. Hooks, define(), and require/ + // include claim it; plain calls and assignments do not. Matches the legacy + // parser (see the filters/actions/constants-includes golden fixtures). + $cases = array( + "apply_filters( 'h', \$x );" => '', // hook claims it + "define( 'X', 1 );" => '', // constant claims it + "require_once 'x.php';" => '', // include claims it + "my_func( \$x );" => 'Doc.', // plain call: floats to the file + "\$x = 1;" => 'Doc.', // assignment: floats to the file + ); + + foreach ( $cases as $statement => $expected ) { + $tmp = tempnam( sys_get_temp_dir(), 'wpp' ); + file_put_contents( $tmp, "setFilename( 'fd.php' ); + $file->process(); + } finally { + unlink( $tmp ); + } + + $doc = $file->getDocBlock(); + $actual = $doc ? $doc->getShortDescription() : ''; + + $this->assertSame( $expected, $actual, "first statement: {$statement}" ); + } + } + + public function test_namespaced_function_and_hook_resolution() { + // In a namespace, function-use names resolve to a leading-backslash FQN unless + // they are unqualified global-fallback calls (which stay bare). A fully- + // qualified \do_action is a plain function call, not a hook. (Golden coverage: + // tests/.../export/namespaced-uses.) + $tmp = tempnam( sys_get_temp_dir(), 'wpp' ); + file_put_contents( + $tmp, + "setFilename( 'ns.php' ); + $file->process(); + } finally { + unlink( $tmp ); + } + + $function = $file->getFunctions()[0]; + $this->assertSame( 'My\\Ns', $function->getNamespace() ); + + $names = array_map( + static function ( $use ) { + return $use->getName(); + }, + $function->uses['functions'] + ); + $this->assertSame( array( '\\Other\\helper', '\\do_action', 'count' ), $names ); + + // The fully-qualified \do_action is a function use, not a hook. + $this->assertEmpty( isset( $function->uses['hooks'] ) ? $function->uses['hooks'] : array() ); + } +} diff --git a/tests/unit/name-resolver-test.php b/tests/unit/name-resolver-test.php new file mode 100644 index 00000000..8aa3e178 --- /dev/null +++ b/tests/unit/name-resolver-test.php @@ -0,0 +1,63 @@ +namespacedName`. + * Function_Call_Reflector (Stage 6) reads that attribute. This pins the behavior. + * + * @package WP_Parser\Tests\Unit + */ + +namespace WP_Parser\Tests\Unit; + +use PhpParser\Node; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitor\NameResolver; +use PhpParser\ParserFactory; +use PHPUnit\Framework\TestCase; + +class Name_Resolver_Test extends TestCase { + + /** + * Resolve a snippet with NameResolver in non-replacing mode. + * + * @param string $code PHP source (without the opening tag). + * @return Node\Stmt[] + */ + private function resolve( $code ) { + $stmts = ( new ParserFactory() )->createForNewestSupportedVersion()->parse( "addVisitor( new NameResolver( null, array( 'replaceNodes' => false ) ) ); + + return $traverser->traverse( $stmts ); + } + + public function test_attaches_namespaced_name_to_unqualified_call() { + $stmts = $this->resolve( "namespace Awesome\\Space;\nohai();" ); + + $call = $stmts[0]->stmts[0]->expr; // Namespace_ -> Expression -> FuncCall + $this->assertInstanceOf( Node\Expr\FuncCall::class, $call ); + + // Original Name node is preserved (replaceNodes:false). + $this->assertInstanceOf( Node\Name::class, $call->name ); + + // Unqualified calls in a namespace cannot be statically resolved (global + // fallback), so they receive a namespacedName attribute. + $namespaced = $call->name->getAttribute( 'namespacedName' ); + $this->assertNotNull( $namespaced ); + $this->assertSame( 'Awesome\\Space\\ohai', $namespaced->toString() ); + } + + public function test_resolves_fully_qualified_call() { + $stmts = $this->resolve( "namespace Awesome\\Space;\n\\Other\\Place\\greet();" ); + + $call = $stmts[0]->stmts[0]->expr; + $resolved = $call->name->getAttribute( 'resolvedName' ); + + $this->assertNotNull( $resolved ); + $this->assertSame( 'Other\\Place\\greet', $resolved->toString() ); + } +} diff --git a/tests/unit/pretty-printer-test.php b/tests/unit/pretty-printer-test.php new file mode 100644 index 00000000..89d0fd50 --- /dev/null +++ b/tests/unit/pretty-printer-test.php @@ -0,0 +1,63 @@ +createForNewestSupportedVersion()->parse( "expr; // Stmt\Expression -> Expr\FuncCall + } + + public function test_pretty_prints_simple_string_hook_name() { + $call = $this->first_call( "do_action( 'plain_action' );" ); + $printer = new Pretty_Printer(); + + $this->assertSame( "'plain_action'", $printer->prettyPrintExpr( $call->args[0]->value ) ); + } + + public function test_pretty_prints_concatenated_hook_name() { + $call = $this->first_call( "do_action( \$variable . '-action' );" ); + $printer = new Pretty_Printer(); + + $this->assertSame( "\$variable . '-action'", $printer->prettyPrintExpr( $call->args[0]->value ) ); + } + + public function test_pretty_prints_filter_arguments() { + $call = $this->first_call( "apply_filters( 'plain_filter', \$variable, \$filter_context );" ); + $printer = new Pretty_Printer(); + + $args = array_map( + function ( Node\Arg $arg ) use ( $printer ) { + return $printer->prettyPrintArg( $arg ); + }, + $call->args + ); + + // The leading name arg is shifted off by Hook_Reflector; here we assert the + // full rendered list, matching the golden contract for plain_filter. + $this->assertSame( array( "'plain_filter'", '$variable', '$filter_context' ), $args ); + } +}