diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..59a7ff3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,41 @@ +version: 2 +updates: + # Enable version updates for npm + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + groups: + wordpress: + patterns: + - "@wordpress/*" + update-types: + - "minor" + - "patch" + + # Enable version updates for Composer + - package-ecosystem: "composer" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + groups: + phpstan: + patterns: + - "phpstan/*" + update-types: + - "minor" + - "patch" + phpunit: + patterns: + - "phpunit/*" + update-types: + - "minor" + - "patch" + + # Enable version updates for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" \ No newline at end of file diff --git a/.github/workflows/import-test.yml b/.github/workflows/import-test.yml new file mode 100644 index 0000000..79ca9f1 --- /dev/null +++ b/.github/workflows/import-test.yml @@ -0,0 +1,130 @@ +name: Import Test + +on: + pull_request: + push: + branches: [ master ] + workflow_dispatch: + +jobs: + test-import: + runs-on: ubuntu-latest + timeout-minutes: 10 + + env: + WP_ENV_PHP_VERSION: '8.2' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install Node.js dependencies + run: npm ci + + - name: Start WordPress environment + run: | + npm run wp-env start + sleep 10 + + - name: Wait for WordPress to be ready + run: | + timeout 60 bash -c 'until curl -s http://localhost:8888 > /dev/null; do sleep 2; done' + + - name: Activate plugins + run: | + npm run wp-env run cli -- -- wp plugin activate phpdoc-parser posts-to-posts + + - name: Debug plugin and command availability + run: | + echo "Checking plugin status..." + npm run wp-env run cli -- -- wp plugin list + echo "Checking available commands..." + npm run wp-env run cli -- -- wp help + echo "Checking if parser command exists..." + npm run wp-env run cli -- -- wp help parser || echo "Parser command not found" + + - name: Run sample import (subset of files) + run: | + # Import a small subset of WordPress core files for testing + npm run wp-env run cli -- -- wp parser create /var/www/html/wp-includes/functions.php --user=admin --quick + + - name: Verify import worked + run: | + # Check that functions were imported + FUNCTION_COUNT=$(npm run wp-env run cli -- -- wp post list --post_type=wp-parser-function --format=count | tail -1) + echo "Functions imported: $FUNCTION_COUNT" + + if [ "$FUNCTION_COUNT" -lt 50 ]; then + echo "ERROR: Expected at least 50 functions, got $FUNCTION_COUNT" + exit 1 + fi + + # Check that classes were imported + CLASS_COUNT=$(npm run wp-env run cli -- -- wp post list --post_type=wp-parser-class --format=count | tail -1) + echo "Classes imported: $CLASS_COUNT" + + # Check that hooks were imported + HOOK_COUNT=$(npm run wp-env run cli -- -- wp post list --post_type=wp-parser-hook --format=count | tail -1) + echo "Hooks imported: $HOOK_COUNT" + + # Check that methods were imported + METHOD_COUNT=$(npm run wp-env run cli -- -- wp post list --post_type=wp-parser-method --format=count | tail -1) + echo "Methods imported: $METHOD_COUNT" + + - name: Test parser command with error detection + run: | + # Run parser and capture both stdout and stderr + if ! npm run wp-env run cli -- -- wp parser create /var/www/html/wp-includes/class-wp.php --user=admin 2>&1 | tee import_output.log; then + echo "ERROR: Parser command failed" + exit 1 + fi + + # Check for PHP warnings or errors in output + if grep -i "warning\|error\|fatal" import_output.log | grep -v "WP_CLI"; then + echo "ERROR: PHP warnings or errors detected in parser output" + cat import_output.log + exit 1 + fi + + echo "✓ Parser completed without PHP warnings or errors" + + - name: Verify specific function exists + run: | + # Test that a well-known WordPress function was parsed correctly from functions.php + FUNCTION_EXISTS=$(npm run wp-env run cli -- -- wp post list --post_type=wp-parser-function --s=wp_date --format=count | tail -1) + if [ "$FUNCTION_EXISTS" -eq 0 ]; then + echo "ERROR: wp_date function not found in parsed data" + exit 1 + fi + echo "✓ wp_date function successfully parsed" + + - name: Test database integrity + run: | + # Check that taxonomies are properly assigned + FILE_TERMS=$(npm run wp-env run cli -- -- wp term list wp-parser-source-file --format=count | tail -1) + echo "File taxonomy terms: $FILE_TERMS" + + if [ "$FILE_TERMS" -eq 0 ]; then + echo "ERROR: No file taxonomy terms found" + exit 1 + fi + + echo "✓ Database integrity checks passed" + + - name: Test WP-CLI functionality + run: | + # Test that WP-CLI is working with the parser + npm run wp-env run cli -- -- wp parser --help | grep -q "create" + echo "✓ WP-CLI parser command available" + + - name: Cleanup on failure + if: failure() + run: | + echo "Import test failed. Checking environment status..." + docker ps \ No newline at end of file diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 9e5ea6a..b6592be 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -3,31 +3,38 @@ name: Unit Tests on: pull_request: push: + branches: [ master ] workflow_dispatch: jobs: - test-php: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - php: - - '7.4' - - env: - WP_ENV_PHP_VERSION: ${{ matrix.php }} - - steps: + test-php: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: + - '8.1' + - '8.2' + - '8.3' + + env: + WP_ENV_PHP_VERSION: ${{ matrix.php }} + + steps: - name: Checkout uses: actions/checkout@v4 - - name: Install - run: npm install + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install Node.js dependencies + run: npm ci - - name: Setup Environment - run: | - rm composer.lock - npm run setup + - name: Setup WordPress environment + run: npm run setup - - name: Test + - name: Run tests run: npm run test diff --git a/.gitignore b/.gitignore index 82f9b22..a1df23b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ vendor coverage node_modules +.phpunit.result.cache +composer.lock diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..3325bb6 --- /dev/null +++ b/.npmrc @@ -0,0 +1,14 @@ +# Use exact versions for reproducible builds +save-exact=true + +# Automatically install peer dependencies +auto-install-peers=true + +# Use npm audit signatures +audit-level=moderate + +# Progress display for CI environments +progress=false + +# Engine strict mode +engine-strict=true \ No newline at end of file diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2edeafb --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20 \ No newline at end of file diff --git a/.wp-env.json b/.wp-env.json index aaf14fa..0fbfa8d 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -1,7 +1,11 @@ { - "phpVersion": "7.4", + "phpVersion": "8.2", "plugins": [ - ".", - "https://downloads.wordpress.org/plugin/posts-to-posts.latest-stable.zip" - ] + "https://downloads.wordpress.org/plugin/posts-to-posts.latest-stable.zip", + "." + ], + "config": { + "WP_DEBUG": false, + "WP_DEBUG_DISPLAY": false + } } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e30108f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,345 @@ +# Contributing to WP Parser + +Thank you for your interest in contributing to WP Parser! This document provides guidelines for contributing to the project. + +## Getting Started + +### Prerequisites + +Before you begin, ensure you have: + +- **PHP 8.1+** installed locally (for development outside Docker) +- **Node.js 20+** and **npm 9+** +- **Docker Desktop** (for wp-env testing environment) +- **Git** for version control +- Basic understanding of PHP, WordPress development, and PHPDoc + +### Development Setup + +1. **Fork and Clone** + ```bash + git clone https://github.com/your-username/phpdoc-parser.git + cd phpdoc-parser + ``` + +2. **Install Dependencies** + ```bash + # Install Node.js dependencies + npm install + + # Install PHP dependencies via wp-env + npm run composer:setup + ``` + +3. **Start Development Environment** + ```bash + # Start WordPress with Docker + npm start + + # Verify everything works + npm test + ``` + +## Development Workflow + +### Making Changes + +1. **Create a branch** for your feature or bug fix: + ```bash + git checkout -b feature/your-feature-name + # or + git checkout -b fix/issue-description + ``` + +2. **Make your changes** following the coding standards below + +3. **Test your changes**: + ```bash + # Run full test suite + npm test + + # Run specific tests + npm run test:phpunit -- --filter=YourTestName + + # Watch tests during development + composer run test:watch + ``` + +4. **Commit your changes** with descriptive messages: + ```bash + git add . + git commit -m "Add support for parsing readonly properties" + ``` + +### Testing + +All contributions must include appropriate tests: + +- **Unit tests** for new parser functionality +- **Integration tests** for WordPress-specific features +- **Regression tests** for bug fixes + +```bash +# Run tests in different ways +npm test # Full test suite +npm run test:phpunit:setup # First-time setup + tests +composer run test:coverage # Generate coverage report + +# Debug failing tests +npm run test:phpunit -- --filter=test_name --debug +``` + +### Code Quality + +Before submitting, ensure your code passes all quality checks: + +```bash +# Currently, the project doesn't have linting configured +# But ensure your code follows WordPress coding standards +``` + +## Coding Standards + +### PHP Code Style + +Follow [WordPress PHP Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/php/): + +- **Indentation**: Use tabs, not spaces +- **Line length**: Aim for 100 characters max +- **Naming**: Use snake_case for functions/variables, PascalCase for classes +- **Documentation**: All public methods must have PHPDoc blocks + +```php +/** + * Parse a function node and extract function information. + * + * @param Node\Stmt\Function_ $node The function node. + * @return array Function data with name, parameters, and docblock. + */ +protected function processFunction( Node\Stmt\Function_ $node ) { + // Implementation +} +``` + +### Documentation + +- **PHPDoc blocks** required for all public methods +- **Inline comments** for complex logic +- **README updates** for new features +- **Example usage** in docblocks when helpful + +### Git Commit Messages + +Use clear, descriptive commit messages: + +```bash +# Good +git commit -m "Add namespace detection to File_Reflector" +git commit -m "Fix property docblock parsing for static properties" +git commit -m "Update README with Docker setup instructions" + +# Avoid +git commit -m "Fix bug" +git commit -m "Update stuff" +git commit -m "WIP" +``` + +## Architecture Guidelines + +### Core Components + +Understanding the parser architecture helps when contributing: + +``` +lib/ +├── class-file-reflector.php # Main AST parser (NodeVisitorAbstract) +├── class-hook-reflector.php # WordPress hook detection +├── class-function-call-reflector.php # Function call parsing +├── class-method-call-reflector.php # Instance method calls +├── class-static-method-call-reflector.php # Static method calls +├── runner.php # API compatibility layer +├── class-importer.php # WordPress post creation +└── template.php # Output formatting +``` + +### Adding New Parsing Features + +When adding new parser functionality: + +1. **Extend File_Reflector** if it needs AST traversal +2. **Create dedicated reflector classes** for complex parsing +3. **Update runner.php** to maintain API compatibility +4. **Add comprehensive tests** in `tests/phpunit/tests/` + +### Parsing Approach + +The parser uses modern PHP libraries: + +- **PHPParser v5**: AST-based parsing for accuracy +- **phpstan/phpdoc-parser**: Advanced PHPDoc understanding +- **NodeVisitorAbstract**: Traverse syntax trees efficiently + +```php +// Example: Adding new node type handling +public function enterNode( Node $node ) { + switch ( $node->getType() ) { + case 'Stmt_YourNewType': + $this->processYourNewType( $node ); + break; + } +} +``` + +## WordPress Integration + +### Testing with wp-env + +The project uses [@wordpress/env](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/) for WordPress integration: + +```bash +# WordPress environment management +npm run wp-env start # Start containers +npm run wp-env stop # Stop containers +npm run wp-env clean # Reset everything + +# Access WordPress container +npm run wp-env run tests-wordpress bash + +# Run WP-CLI commands +npm run wp-env run tests-wordpress wp plugin list +``` + +### Parser WordPress Integration + +- **Plugin activation**: Tests run in a real WordPress environment +- **Post creation**: Parsed data becomes WordPress posts +- **Hook detection**: WordPress-specific action/filter parsing +- **Database storage**: Results stored as custom post types + +## Common Tasks + +### Adding Support for New PHP Features + +Example: Adding enum support + +1. **Update File_Reflector** to detect enum nodes: + ```php + case 'Stmt_Enum': + $this->processEnum( $node ); + break; + ``` + +2. **Create processing method**: + ```php + protected function processEnum( Node\Stmt\Enum_ $node ) { + // Extract enum data + } + ``` + +3. **Update export functions** in runner.php +4. **Add test cases** for various enum scenarios + +### Improving WordPress Hook Detection + +Hook detection happens in `isFilter()` method: + +```php +protected function isFilter( Node\Expr\FuncCall $node ) { + $function_name = $node->name->toString(); + return in_array( $function_name, [ + 'apply_filters', + 'do_action', + 'add_filter', + 'add_action' + // Add new hook functions here + ], true ); +} +``` + +### Debugging Parser Issues + +1. **Add debug logging**: + ```php + error_log( 'Debug: Found node type: ' . $node->getType() ); + ``` + +2. **Inspect AST structure**: + ```bash + # Use php-parse to see AST + vendor/bin/php-parse /path/to/file.php + ``` + +3. **Check test output**: + ```bash + npm run test:phpunit -- --filter=failing_test --debug + ``` + +## Submitting Contributions + +### Pull Request Process + +1. **Ensure tests pass**: `npm test` should be green +2. **Update documentation** if needed +3. **Create detailed PR description**: + - What changes were made + - Why they were needed + - How to test the changes + - Any breaking changes + +4. **Link related issues**: Use "Fixes #123" in description + +### PR Review Criteria + +Your PR will be reviewed for: + +- **Functionality**: Does it work as intended? +- **Testing**: Are there adequate tests? +- **Code quality**: Follows coding standards? +- **Documentation**: Is it properly documented? +- **WordPress compatibility**: Works with WordPress ecosystem? +- **Performance**: No significant performance regressions? + +## Getting Help + +### Resources + +- **WordPress Developer Handbook**: https://make.wordpress.org/docs/handbook/projects/devhub/ +- **PHPParser Documentation**: https://github.com/nikic/PHP-Parser/tree/master/doc +- **phpstan/phpdoc-parser**: https://github.com/phpstan/phpdoc-parser +- **WordPress Coding Standards**: https://developer.wordpress.org/coding-standards/ + +### Community + +- **GitHub Issues**: For bug reports and feature requests +- **WordPress Slack**: #docs channel for general discussion +- **GitHub Discussions**: For questions and implementation discussions + +### Common Issues + +**Docker Problems**: +```bash +npm run wp-env clean +npm run wp-env start +``` + +**PHP Version Issues**: +- wp-env uses PHP 8.2 in containers +- Ensure Docker is running +- For local development, ensure PHP 8.1+ + +**Test Failures**: +- Check that WordPress environment is running +- Verify all dependencies are installed +- Look at specific test output for clues + +## Release Process + +For maintainers: + +1. **Update version** in relevant files +2. **Update changelog** with new features/fixes +3. **Tag release**: `git tag v1.x.x` +4. **Create GitHub release** with changelog +5. **Test on WordPress.org** staging environment + +Thank you for contributing to WP Parser! Your efforts help improve the WordPress developer experience for thousands of developers worldwide. \ No newline at end of file diff --git a/README.md b/README.md index 6c4390b..96e2f67 100644 --- a/README.md +++ b/README.md @@ -2,32 +2,202 @@ WP-Parser is the parser for creating the new code reference at [developer.wordpress.org](https://developer.wordpress.org/reference). It parses the inline documentation and produces custom post type entries in WordPress. -We are currently looking for contributors to help us complete the work on the parser. - -There is a guide to developing for developer.wordpress.org in the [WordPress documentation handbook](https://make.wordpress.org/docs/handbook/projects/devhub/) +The parser supports PHP 8.1+ and uses modern parsing libraries for improved accuracy and maintainability. ## Requirements -* PHP 5.4+ + +* **PHP 8.1+** +* **Node.js 20+** and **npm 9+** (for development environment) +* **Docker** (for wp-env testing environment) * [Composer](https://getcomposer.org/) -* [WP CLI](https://wp-cli.org/) -Clone the repository into your WordPress plugins directory: +## Quick Start + +### 1. Clone the Repository ```bash git clone https://github.com/WordPress/phpdoc-parser.git +cd phpdoc-parser +``` + +### 2. Install Dependencies + +Install both Node.js and PHP dependencies: + +```bash +# Install Node.js dependencies (includes @wordpress/env) +npm install + +# Install PHP dependencies +npm run composer:setup +# OR if you have local PHP 8.1+: composer install +``` + +### 3. Start Development Environment + +The project uses [@wordpress/env](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/) (wp-env) for local development: + +```bash +# Start WordPress environment with Docker +npm start + +# Or manually: +npm run wp-env start +``` + +This will start two WordPress environments: +- **Development**: `http://localhost:8888` (admin: `http://localhost:8888/wp-admin/` - admin/password) +- **Tests**: `http://localhost:8889` (for automated testing only) + +### 4. Run Tests + +```bash +# Run the full test suite +npm test + +# Run tests with setup (first time) +npm run test:phpunit:setup + +# Watch tests during development +composer run test:watch +``` + +## Development + +### Architecture + +The parser uses: + +- **PHP 8.1+ support** with modern language features +- **PHPParser v5** for Abstract Syntax Tree (AST) parsing +- **phpstan/phpdoc-parser** for advanced PHPDoc analysis +- **PHPUnit 9** for testing compatibility with WordPress + +### Key Components + +- `lib/class-file-reflector.php` - Main file parser using AST traversal +- `lib/class-hook-reflector.php` - WordPress hook detection +- `lib/class-*-reflector.php` - Various reflectors for functions, methods, calls +- `lib/runner.php` - API compatibility layer and export functions + +### Running the Parser + +The parser runs via WP-CLI commands in the **development environment** (port 8888). There are two ways to run WP-CLI commands: + +#### Option 1: Using Host WP-CLI (Recommended) + +The project includes a `wp-cli.yml` configuration file that connects to the development environment: + +```bash +# Activate plugins (requires wp-cli installed on host) +wp plugin activate phpdoc-parser posts-to-posts + +# Parse WordPress core files +wp parser create /var/www/html --user=admin --quick + +# Parse specific directory +wp parser create /path/to/plugin/source --user=admin + +# View parsed results at http://localhost:8888/wp-admin/ +``` + +#### Option 2: Using Docker Container + +```bash +# Find the development WordPress container ID +WORDPRESS_CONTAINER=$(docker ps --filter "name=wordpress-1" --format "{{.ID}}") + +# Activate plugins in development environment +docker exec $WORDPRESS_CONTAINER wp plugin activate phpdoc-parser posts-to-posts + +# Parse WordPress core files in development environment +docker exec $WORDPRESS_CONTAINER wp parser create /var/www/html --user=admin --quick +``` + +**Important**: Always use the development environment (8888) for WP-CLI operations. The test environment (8889) is only for automated PHPUnit tests. + +### Testing + +The project includes comprehensive tests that validate parsing accuracy: + +```bash +# Run all tests +npm test + +# Run specific test +npm run test:phpunit -- --filter=test_function_docblocks + +# Generate coverage report +composer run test:coverage +``` + +### Using wp-env Commands + +```bash +# Access development WordPress container +npm run wp-env run wordpress bash + +# Access test WordPress container (for debugging tests only) +npm run wp-env run tests-wordpress bash + +# Run WP-CLI in development environment +npm run wp-env run wordpress wp --info + +# Stop environment +npm run wp-env stop + +# Reset environment +npm run wp-env clean +``` + +### Environment Usage + +- **Development (localhost:8888)**: Use for plugin development, WP-CLI commands, and manual testing +- **Tests (localhost:8889)**: Automatically used by `npm test` - don't run manual commands here + +## Parsed Output + +The parser extracts: + +- **Functions** with parameters, return types, and docblocks +- **Classes** with methods, properties, and inheritance +- **WordPress Hooks** (actions and filters) with documentation +- **Method/Function calls** and their relationships +- **Namespaces** and class hierarchies +- **PHPDoc tags** (@param, @return, @since, etc.) + +Output is compatible with the WordPress.org developer reference format. + +## Troubleshooting + +### Docker Issues +```bash +# Reset wp-env if having issues +npm run wp-env clean +npm run wp-env start ``` -After that install the dependencies using composer in the parser directory: +### PHP Version Issues +The parser requires PHP 8.1+. If you see PHP version errors: +- Ensure Docker is running (wp-env uses PHP 8.2 in containers) +- For local development, install PHP 8.1+ locally +### Memory Issues +For large codebases, you may need to increase PHP memory: ```bash -composer install +# In wp-env container +npm run wp-env run tests-wordpress bash +php -d memory_limit=512M vendor/bin/phpunit ``` -## Running -Activate the plugin first: +## Contributing - wp plugin activate phpdoc-parser +Please see [CONTRIBUTING.md](CONTRIBUTING.md) for development guidelines, coding standards, and how to submit contributions. -In your site's directory: +## Resources - wp parser create /path/to/source/code --user= +- [WordPress Developer Handbook](https://make.wordpress.org/docs/handbook/projects/devhub/) +- [WordPress.org Developer Reference](https://developer.wordpress.org/reference/) +- [@wordpress/env Documentation](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/) +- [PHPParser Documentation](https://github.com/nikic/PHP-Parser) +- [PHPStan PHPDoc Parser](https://github.com/phpstan/phpdoc-parser) \ No newline at end of file diff --git a/composer.json b/composer.json index aa09726..0b4ad9c 100644 --- a/composer.json +++ b/composer.json @@ -20,16 +20,14 @@ "issues": "https://github.com/WordPress/phpdoc-parser/issues" }, "require" : { - "php" : ">=5.4", - "composer/installers" : "~1.0", - "phpdocumentor/reflection" : "~3.0", - "erusev/parsedown" : "~1.7", - "scribu/lib-posts-to-posts": "dev-master@dev", - "scribu/scb-framework" : "dev-master@dev", - "psr/log" : "~1.0" + "php" : ">=8.1", + "composer/installers" : "~2.0", + "phpstan/phpdoc-parser" : "^2.0", + "nikic/php-parser" : "^5.0", + "psr/log" : "^2.0|^3.0" }, "require-dev" : { - "phpunit/phpunit": "^7", + "phpunit/phpunit": "^9.0", "spatie/phpunit-watcher": "^1.23", "yoast/phpunit-polyfills": "^1.0" }, diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 55aea9d..0000000 --- a/composer.lock +++ /dev/null @@ -1,3655 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "96d200642f6aded313abd6eb270909ee", - "packages": [ - { - "name": "composer/installers", - "version": "v1.12.0", - "source": { - "type": "git", - "url": "https://github.com/composer/installers.git", - "reference": "d20a64ed3c94748397ff5973488761b22f6d3f19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/installers/zipball/d20a64ed3c94748397ff5973488761b22f6d3f19", - "reference": "d20a64ed3c94748397ff5973488761b22f6d3f19", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0 || ^2.0" - }, - "replace": { - "roundcube/plugin-installer": "*", - "shama/baton": "*" - }, - "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" - }, - "type": "composer-plugin", - "extra": { - "class": "Composer\\Installers\\Plugin", - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Installers\\": "src/Composer/Installers" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kyle Robinson Young", - "email": "kyle@dontkry.com", - "homepage": "https://github.com/shama" - } - ], - "description": "A multi-framework Composer library installer", - "homepage": "https://composer.github.io/installers/", - "keywords": [ - "Craft", - "Dolibarr", - "Eliasis", - "Hurad", - "ImageCMS", - "Kanboard", - "Lan Management System", - "MODX Evo", - "MantisBT", - "Mautic", - "Maya", - "OXID", - "Plentymarkets", - "Porto", - "RadPHP", - "SMF", - "Starbug", - "Thelia", - "Whmcs", - "WolfCMS", - "agl", - "aimeos", - "annotatecms", - "attogram", - "bitrix", - "cakephp", - "chef", - "cockpit", - "codeigniter", - "concrete5", - "croogo", - "dokuwiki", - "drupal", - "eZ Platform", - "elgg", - "expressionengine", - "fuelphp", - "grav", - "installer", - "itop", - "joomla", - "known", - "kohana", - "laravel", - "lavalite", - "lithium", - "magento", - "majima", - "mako", - "mediawiki", - "miaoxing", - "modulework", - "modx", - "moodle", - "osclass", - "pantheon", - "phpbb", - "piwik", - "ppi", - "processwire", - "puppet", - "pxcms", - "reindex", - "roundcube", - "shopware", - "silverstripe", - "sydes", - "sylius", - "symfony", - "tastyigniter", - "typo3", - "wordpress", - "yawik", - "zend", - "zikula" - ], - "support": { - "issues": "https://github.com/composer/installers/issues", - "source": "https://github.com/composer/installers/tree/v1.12.0" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2021-09-13T08:19:44+00:00" - }, - { - "name": "erusev/parsedown", - "version": "1.7.4", - "source": { - "type": "git", - "url": "https://github.com/erusev/parsedown.git", - "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3", - "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35" - }, - "type": "library", - "autoload": { - "psr-0": { - "Parsedown": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Emanuil Rusev", - "email": "hello@erusev.com", - "homepage": "http://erusev.com" - } - ], - "description": "Parser for Markdown.", - "homepage": "http://parsedown.org", - "keywords": [ - "markdown", - "parser" - ], - "support": { - "issues": "https://github.com/erusev/parsedown/issues", - "source": "https://github.com/erusev/parsedown/tree/1.7.x" - }, - "time": "2019-12-30T22:54:17+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v1.4.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", - "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "files": [ - "lib/bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/1.x" - }, - "time": "2015-09-19T14:15:08+00:00" - }, - { - "name": "phpdocumentor/reflection", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/Reflection.git", - "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", - "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", - "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" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/", - "tests/unit/", - "tests/mocks/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Reflection library to do Static Analysis for PHP Projects", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/Reflection/issues", - "source": "https://github.com/phpDocumentor/Reflection/tree/master" - }, - "time": "2016-05-21T08:42:32+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "2.0.5", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e6a969a640b00d8daa3c66518b0405fb41ae0c4b", - "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "dflydev/markdown": "~1.0", - "erusev/parsedown": "~1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" - } - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/2.x" - }, - "time": "2016-01-25T08:17:30+00:00" - }, - { - "name": "psr/log", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" - }, - "time": "2021-05-03T11:20:27+00:00" - }, - { - "name": "scribu/lib-posts-to-posts", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/scribu/wp-lib-posts-to-posts.git", - "reference": "a695438e455587fa228e993d05b4431cde99af1b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/scribu/wp-lib-posts-to-posts/zipball/a695438e455587fa228e993d05b4431cde99af1b", - "reference": "a695438e455587fa228e993d05b4431cde99af1b", - "shasum": "" - }, - "require": { - "scribu/scb-framework": "dev-master" - }, - "default-branch": true, - "type": "library", - "autoload": { - "files": [ - "autoload.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-2.0+" - ], - "description": "A library for many-to-many relationships in WordPress", - "homepage": "https://github.com/scribu/wp-lib-posts-to-posts", - "support": { - "source": "https://github.com/scribu/wp-lib-posts-to-posts/tree/master" - }, - "time": "2016-02-15T12:08:59+00:00" - }, - { - "name": "scribu/scb-framework", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/scribu/wp-scb-framework.git", - "reference": "d35d5126c6d323711dd14b9d97aaa927eaaba123" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/scribu/wp-scb-framework/zipball/d35d5126c6d323711dd14b9d97aaa927eaaba123", - "reference": "d35d5126c6d323711dd14b9d97aaa927eaaba123", - "shasum": "" - }, - "default-branch": true, - "type": "library", - "autoload": { - "files": [ - "load-composer.php", - "Util.php" - ], - "classmap": [ - "." - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-3.0+" - ], - "authors": [ - { - "name": "Cristi Burcă", - "homepage": "http://scribu.net/" - } - ], - "description": "A set of useful classes for faster plugin development", - "homepage": "https://github.com/scribu/wp-scb-framework", - "keywords": [ - "wordpress" - ], - "support": { - "issues": "https://github.com/scribu/wp-scb-framework/issues", - "source": "https://github.com/scribu/wp-scb-framework", - "wiki": "https://github.com/scribu/wp-scb-framework/wiki" - }, - "time": "2020-03-15T20:51:58+00:00" - } - ], - "packages-dev": [ - { - "name": "clue/stdio-react", - "version": "v2.6.0", - "source": { - "type": "git", - "url": "https://github.com/clue/reactphp-stdio.git", - "reference": "dfa6c378aabdff718202d4e2453f752c38ea3399" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-stdio/zipball/dfa6c378aabdff718202d4e2453f752c38ea3399", - "reference": "dfa6c378aabdff718202d4e2453f752c38ea3399", - "shasum": "" - }, - "require": { - "clue/term-react": "^1.0 || ^0.1.1", - "clue/utf8-react": "^1.0 || ^0.1", - "php": ">=5.3", - "react/event-loop": "^1.2", - "react/stream": "^1.2" - }, - "require-dev": { - "clue/arguments": "^2.0", - "clue/commander": "^1.2", - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "suggest": { - "ext-mbstring": "Using ext-mbstring should provide slightly better performance for handling I/O" - }, - "type": "library", - "autoload": { - "psr-4": { - "Clue\\React\\Stdio\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering" - } - ], - "description": "Async, event-driven console input & output (STDIN, STDOUT) for truly interactive CLI applications, built on top of ReactPHP", - "homepage": "https://github.com/clue/reactphp-stdio", - "keywords": [ - "async", - "autocomplete", - "autocompletion", - "cli", - "history", - "interactive", - "reactphp", - "readline", - "stdin", - "stdio", - "stdout" - ], - "support": { - "issues": "https://github.com/clue/reactphp-stdio/issues", - "source": "https://github.com/clue/reactphp-stdio/tree/v2.6.0" - }, - "funding": [ - { - "url": "https://clue.engineering/support", - "type": "custom" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2022-03-18T15:09:30+00:00" - }, - { - "name": "clue/term-react", - "version": "v1.3.0", - "source": { - "type": "git", - "url": "https://github.com/clue/reactphp-term.git", - "reference": "eb6eb063eda04a714ef89f066586a2c49588f7ca" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-term/zipball/eb6eb063eda04a714ef89f066586a2c49588f7ca", - "reference": "eb6eb063eda04a714ef89f066586a2c49588f7ca", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "react/stream": "^1.0 || ^0.7" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8", - "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Clue\\React\\Term\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering" - } - ], - "description": "Streaming terminal emulator, built on top of ReactPHP.", - "homepage": "https://github.com/clue/reactphp-term", - "keywords": [ - "C0", - "CSI", - "ansi", - "apc", - "ascii", - "c1", - "control codes", - "dps", - "osc", - "pm", - "reactphp", - "streaming", - "terminal", - "vt100", - "xterm" - ], - "support": { - "issues": "https://github.com/clue/reactphp-term/issues", - "source": "https://github.com/clue/reactphp-term/tree/v1.3.0" - }, - "funding": [ - { - "url": "https://clue.engineering/support", - "type": "custom" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2020-11-06T11:50:12+00:00" - }, - { - "name": "clue/utf8-react", - "version": "v1.2.0", - "source": { - "type": "git", - "url": "https://github.com/clue/reactphp-utf8.git", - "reference": "8bc3f8c874cdf642c8f10f9ae93aadb8cd63da96" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-utf8/zipball/8bc3f8c874cdf642c8f10f9ae93aadb8cd63da96", - "reference": "8bc3f8c874cdf642c8f10f9ae93aadb8cd63da96", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4 || ^0.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 ||^5.7 || ^4.8", - "react/stream": "^1.0 || ^0.7" - }, - "type": "library", - "autoload": { - "psr-4": { - "Clue\\React\\Utf8\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering" - } - ], - "description": "Streaming UTF-8 parser, built on top of ReactPHP.", - "homepage": "https://github.com/clue/reactphp-utf8", - "keywords": [ - "reactphp", - "streaming", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "issues": "https://github.com/clue/reactphp-utf8/issues", - "source": "https://github.com/clue/reactphp-utf8/tree/v1.2.0" - }, - "funding": [ - { - "url": "https://clue.engineering/support", - "type": "custom" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2020-11-06T11:48:09+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "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" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-03-03T08:28:38+00:00" - }, - { - "name": "evenement/evenement", - "version": "v3.0.1", - "source": { - "type": "git", - "url": "https://github.com/igorw/evenement.git", - "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", - "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Evenement": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - } - ], - "description": "Événement is a very simple event dispatching library for PHP", - "keywords": [ - "event-dispatcher", - "event-emitter" - ], - "support": { - "issues": "https://github.com/igorw/evenement/issues", - "source": "https://github.com/igorw/evenement/tree/master" - }, - "time": "2017-07-23T21:35:13+00:00" - }, - { - "name": "jolicode/jolinotif", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/jolicode/JoliNotif.git", - "reference": "a15bfc0d5aef432f150385924ede4e099643edb7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/jolicode/JoliNotif/zipball/a15bfc0d5aef432f150385924ede4e099643edb7", - "reference": "a15bfc0d5aef432f150385924ede4e099643edb7", - "shasum": "" - }, - "require": { - "php": ">=7.4", - "symfony/process": "^4.0|^5.0|^6.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0", - "symfony/finder": "^5.0", - "symfony/phpunit-bridge": "^5.0" - }, - "bin": [ - "jolinotif" - ], - "type": "library", - "autoload": { - "psr-4": { - "Joli\\JoliNotif\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Loïck Piera", - "email": "pyrech@gmail.com" - } - ], - "description": "Send desktop notifications on Windows, Linux, MacOS.", - "keywords": [ - "MAC", - "growl", - "linux", - "notification", - "windows" - ], - "support": { - "issues": "https://github.com/jolicode/JoliNotif/issues", - "source": "https://github.com/jolicode/JoliNotif/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/jolicode/jolinotif", - "type": "tidelift" - } - ], - "time": "2021-12-01T16:20:42+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2022-03-03T13:19:32+00:00" - }, - { - "name": "phar-io/manifest", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "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" - }, - "time": "2018-07-08T19:23:20+00:00" - }, - { - "name": "phar-io/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "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" - }, - "time": "2018-07-08T19:19:57+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.10.3", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "451c3cd1418cf640de218914901e51b064abb093" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", - "reference": "451c3cd1418cf640de218914901e51b064abb093", - "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" - }, - "require-dev": { - "phpspec/phpspec": "^2.5 || ^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" - }, - "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": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "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" - }, - { - "name": "phpunit/php-file-iterator", - "version": "2.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", - "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-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": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.5" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-12-02T12:42:26+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" - }, - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "2.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1-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": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:20:02+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "3.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "9c1da83261628cb24b6a6df371b6e312b3954768" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9c1da83261628cb24b6a6df371b6e312b3954768", - "reference": "9c1da83261628cb24b6a6df371b6e312b3954768", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "abandoned": true, - "time": "2021-07-26T12:15:06+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "7.5.20", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9467db479d1b0487c99733bb1e7944d32deded2c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c", - "reference": "9467db479d1b0487c99733bb1e7944d32deded2c", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.1", - "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": "*" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.5-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": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/7.5.20" - }, - "time": "2020-01-08T08:45:45+00:00" - }, - { - "name": "psr/container", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" - }, - "time": "2021-11-05T16:50:12+00:00" - }, - { - "name": "react/event-loop", - "version": "v1.3.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/event-loop.git", - "reference": "187fb56f46d424afb6ec4ad089269c72eec2e137" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/187fb56f46d424afb6ec4ad089269c72eec2e137", - "reference": "187fb56f46d424afb6ec4ad089269c72eec2e137", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "suggest": { - "ext-event": "~1.0 for ExtEventLoop", - "ext-pcntl": "For signal handling support when using the StreamSelectLoop", - "ext-uv": "* for ExtUvLoop" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\EventLoop\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", - "keywords": [ - "asynchronous", - "event-loop" - ], - "support": { - "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.3.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2022-03-17T11:10:22+00:00" - }, - { - "name": "react/stream", - "version": "v1.2.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/stream.git", - "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/7a423506ee1903e89f1e08ec5f0ed430ff784ae9", - "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9", - "shasum": "" - }, - "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.8", - "react/event-loop": "^1.2" - }, - "require-dev": { - "clue/stream-filter": "~1.2", - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Stream\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", - "keywords": [ - "event-driven", - "io", - "non-blocking", - "pipe", - "reactphp", - "readable", - "stream", - "writable" - ], - "support": { - "issues": "https://github.com/reactphp/stream/issues", - "source": "https://github.com/reactphp/stream/tree/v1.2.0" - }, - "funding": [ - { - "url": "https://github.com/WyriHaximus", - "type": "github" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2021-07-11T12:37:55+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "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" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:15:22+00:00" - }, - { - "name": "sebastian/comparator", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:04:30+00:00" - }, - { - "name": "sebastian/diff", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:59:04+00:00" - }, - { - "name": "sebastian/environment", - "version": "4.2.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:53:42+00:00" - }, - { - "name": "sebastian/exporter", - "version": "3.1.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", - "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", - "shasum": "" - }, - "require": { - "php": ">=7.0", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^8.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://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" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-11-11T13:51:24+00:00" - }, - { - "name": "sebastian/global-state", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-uopz": "*" - }, - "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" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/2.0.0" - }, - "time": "2017-04-27T15:39:26+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "3.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "shasum": "" - }, - "require": { - "php": ">=7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "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" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:40:27+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "1.1.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "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" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:37:18+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.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" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:34:24+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "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" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:30:19+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-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 that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" - }, - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "spatie/phpunit-watcher", - "version": "1.23.6", - "source": { - "type": "git", - "url": "https://github.com/spatie/phpunit-watcher.git", - "reference": "c192fff763810c8378511bcf0069df4b91478866" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/spatie/phpunit-watcher/zipball/c192fff763810c8378511bcf0069df4b91478866", - "reference": "c192fff763810c8378511bcf0069df4b91478866", - "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" - }, - "conflict": { - "symfony/console": "<5.2", - "yosymfony/resource-watcher": "<2.0" - }, - "require-dev": { - "phpunit/phpunit": "^8.6 | ^9.0" - }, - "bin": [ - "phpunit-watcher" - ], - "type": "library", - "autoload": { - "psr-4": { - "Spatie\\PhpUnitWatcher\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Freek Van der Herten", - "email": "freek@spatie.be", - "homepage": "https://spatie.be", - "role": "Developer" - } - ], - "description": "Automatically rerun PHPUnit tests when source code changes", - "homepage": "https://github.com/spatie/phpunit-watcher", - "keywords": [ - "phpunit-watcher", - "spatie" - ], - "support": { - "issues": "https://github.com/spatie/phpunit-watcher/issues", - "source": "https://github.com/spatie/phpunit-watcher/tree/1.23.6" - }, - "time": "2022-01-31T11:57:13+00:00" - }, - { - "name": "symfony/console", - "version": "v5.4.12", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "c072aa8f724c3af64e2c7a96b796a4863d24dba1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c072aa8f724c3af64e2c7a96b796a4863d24dba1", - "reference": "c072aa8f724c3af64e2c7a96b796a4863d24dba1", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^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" - }, - "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" - }, - "provide": { - "psr/log-implementation": "1.0|2.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": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command line", - "console", - "terminal" - ], - "support": { - "source": "https://github.com/symfony/console/tree/v5.4.12" - }, - "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-08-17T13:18:05+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "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": "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" - }, - "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-01-02T09:53:40+00:00" - }, - { - "name": "symfony/finder", - "version": "v5.4.11", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/7872a66f57caffa2916a584db1aa7f12adc76f8c", - "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "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" - }, - "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-07-29T07:37:50+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "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\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/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-intl-grapheme", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "433d05519ce6990bf3530fba6957499d327395c2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", - "reference": "433d05519ce6990bf3530fba6957499d327395c2", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "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" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } - }, - "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 for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/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-intl-normalizer", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "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" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "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 for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/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-mbstring", - "version": "v1.26.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "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\\Mbstring\\": "" - } - }, - "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 for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "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" - }, - "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-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", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-05-10T07:21:04+00:00" - }, - { - "name": "symfony/process", - "version": "v5.4.11", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/6e75fe6874cbc7e4773d049616ab450eff537bf1", - "reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v5.4.11" - }, - "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-06-27T16:58:25+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v2.5.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^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" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } - }, - "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": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" - }, - "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-30T19:17:29+00:00" - }, - { - "name": "symfony/string", - "version": "v5.4.12", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "2fc515e512d721bf31ea76bd02fe23ada4640058" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/2fc515e512d721bf31ea76bd02fe23ada4640058", - "reference": "2fc515e512d721bf31ea76bd02fe23ada4640058", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" - }, - "conflict": { - "symfony/translation-contracts": ">=3.0" - }, - "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" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "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": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "source": "https://github.com/symfony/string/tree/v5.4.12" - }, - "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-08-12T17:03:11+00:00" - }, - { - "name": "symfony/yaml", - "version": "v5.4.12", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "7a3aa21ac8ab1a96cc6de5bbcab4bc9fc943b18c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/7a3aa21ac8ab1a96cc6de5bbcab4bc9fc943b18c", - "reference": "7a3aa21ac8ab1a96cc6de5bbcab4bc9fc943b18c", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/console": "<5.3" - }, - "require-dev": { - "symfony/console": "^5.3|^6.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "bin": [ - "Resources/bin/yaml-lint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Loads and dumps YAML files", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.12" - }, - "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-08-02T15:52:22+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "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" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - }, - { - "name": "yoast/phpunit-polyfills", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "5ea3536428944955f969bc764bbe09738e151ada" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/5ea3536428944955f969bc764bbe09738e151ada", - "reference": "5ea3536428944955f969bc764bbe09738e151ada", - "shasum": "" - }, - "require": { - "php": ">=5.4", - "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0" - }, - "require-dev": { - "yoast/yoastcs": "^2.2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev", - "dev-develop": "1.x-dev" - } - }, - "autoload": { - "files": [ - "phpunitpolyfills-autoload.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Team Yoast", - "email": "support@yoast.com", - "homepage": "https://yoast.com" - }, - { - "name": "Contributors", - "homepage": "https://github.com/Yoast/PHPUnit-Polyfills/graphs/contributors" - } - ], - "description": "Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests", - "homepage": "https://github.com/Yoast/PHPUnit-Polyfills", - "keywords": [ - "phpunit", - "polyfill", - "testing" - ], - "support": { - "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", - "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" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": { - "scribu/lib-posts-to-posts": 20, - "scribu/scb-framework": 20 - }, - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=5.4" - }, - "platform-dev": [], - "plugin-api-version": "2.3.0" -} diff --git a/lib/class-file-reflector.php b/lib/class-file-reflector.php index 62f401d..1e23864 100644 --- a/lib/class-file-reflector.php +++ b/lib/class-file-reflector.php @@ -2,22 +2,33 @@ namespace WP_Parser; -use phpDocumentor\Reflection; -use phpDocumentor\Reflection\FileReflector; +use PhpParser\Node; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; +use PhpParser\ParserFactory; +use PhpParser\PrettyPrinter\Standard as PrettyPrinter; +use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode; +use PHPStan\PhpDocParser\Lexer\Lexer; +use PHPStan\PhpDocParser\Parser\ConstExprParser; +use PHPStan\PhpDocParser\Parser\PhpDocParser; +use PHPStan\PhpDocParser\Parser\TokenIterator; +use PHPStan\PhpDocParser\Parser\TypeParser; +use PHPStan\PhpDocParser\ParserConfig; /** - * Reflection class for a full file. + * Modern file parser using PHPParser v5 and phpstan/phpdoc-parser. * - * Extends the FileReflector from phpDocumentor to parse out WordPress - * hooks and note function relationships. + * Parses WordPress files to extract functions, classes, methods, hooks, + * and their relationships for the developer.wordpress.org reference. */ -class File_Reflector extends FileReflector { +class File_Reflector extends NodeVisitorAbstract { + /** * List of elements used in global scope in this file, indexed by element type. * - * @var array { - * @type Hook_Reflector[] $hooks The action and filters. - * @type Function_Call_Reflector[] $functions The functions called. + * @var array{ + * hooks: Hook_Reflector[], + * functions: Function_Call_Reflector[] * } */ public $uses = array(); @@ -25,48 +36,176 @@ class File_Reflector extends FileReflector { /** * List of elements used in the current class scope, indexed by method. * - * @var array[][] {@see \WP_Parser\File_Reflector::$uses} + * @var array */ protected $method_uses_queue = array(); /** * Stack of classes/methods/functions currently being parsed. * - * @see \WP_Parser\FileReflector::getLocation() - * @var \phpDocumentor\Reflection\BaseReflector[] + * @var Node[] */ protected $location = array(); /** * Last DocBlock associated with a non-documentable element. * - * @var \PHPParser_Comment_Doc + * @var Node\Comment\Doc|null */ protected $last_doc = null; /** - * Add hooks to the queue and update the node stack when we enter a node. + * The PHP parser instance. + * + * @var \PhpParser\Parser + */ + protected $parser; + + /** + * The PHPDoc parser instance. + * + * @var PhpDocParser + */ + protected $phpdoc_parser; + + /** + * The pretty printer for code. + * + * @var PrettyPrinter + */ + protected $pretty_printer; + + /** + * File content being parsed. + * + * @var string + */ + protected $content; + + /** + * File path being parsed. + * + * @var string + */ + protected $file_path; + + /** + * Parsed functions from the file. * - * 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. + * @var array + */ + public $functions = array(); + + /** + * Parsed classes from 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. + * @var array + */ + public $classes = array(); + + /** + * Current namespace context. * - * 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. + * @var string|null + */ + protected $current_namespace = null; + + /** + * Initialize the file reflector. * - * @param \PHPParser_Node $node + * @param string $file_path Path to the file to parse. + * @param string $content File content to parse. */ - public function enterNode( \PHPParser_Node $node ) { - parent::enterNode( $node ); + public function __construct( $file_path, $content ) { + $this->file_path = $file_path; + $this->content = $content; + + // Initialize PHP parser + $parser_factory = new ParserFactory(); + $this->parser = $parser_factory->createForNewestSupportedVersion(); + + // Initialize PHPDoc parser + $config = new ParserConfig( usedAttributes: [] ); + $constExprParser = new ConstExprParser( $config ); + $typeParser = new TypeParser( $config, $constExprParser ); + $this->phpdoc_parser = new PhpDocParser( $config, $typeParser, $constExprParser ); + + // Initialize pretty printer + $this->pretty_printer = new PrettyPrinter(); + + $this->uses = array( + 'hooks' => array(), + 'functions' => array(), + 'methods' => array(), + ); + } + + /** + * Parse the file and extract all elements. + * + * @return array Parsed data structure. + */ + public function parse() { + try { + $statements = $this->parser->parse( $this->content ); + if ( null === $statements ) { + return array(); + } + + // Extract file-level docblock - check first statement for leading comments + $file_docblock = null; + if ( ! empty( $statements ) ) { + $first_stmt = $statements[0]; + $comments = $first_stmt->getAttribute( 'comments' ); + + if ( $comments ) { + // Take the first docblock comment as file-level + $first_comment = $comments[0]; + if ( $first_comment instanceof \PhpParser\Comment\Doc ) { + $file_docblock = $this->parseDocComment( $first_comment ); + } + } + } + + $traverser = new NodeTraverser(); + $traverser->addVisitor( $this ); + $traverser->traverse( $statements ); + + return array( + 'functions' => $this->functions, + 'classes' => $this->classes, + 'uses' => $this->uses, + 'file_docblock' => $file_docblock, + ); + } catch ( \Exception $e ) { + // Log error and return empty result + error_log( 'Parse error in ' . $this->file_path . ': ' . $e->getMessage() ); + return array( + 'functions' => array(), + 'classes' => array(), + 'uses' => array(), + 'file_docblock' => null, + ); + } + } + /** + * Called when entering a node during traversal. + * + * @param Node $node The node being entered. + * @return int|null + */ + public function enterNode( Node $node ) { switch ( $node->getType() ) { + // Track namespace declarations + case 'Stmt_Namespace': + if ( $node->name ) { + $this->current_namespace = $node->name->toString(); + } else { + $this->current_namespace = null; + } + break; + // Add classes, functions, and methods to the current location stack case 'Stmt_Class': case 'Stmt_Function': @@ -74,12 +213,19 @@ public function enterNode( \PHPParser_Node $node ) { array_push( $this->location, $node ); break; - // Parse out hook definitions and function calls and add them to the queue. + // Parse out hook definitions and function calls case 'Expr_FuncCall': - $function = new Function_Call_Reflector( $node, $this->context ); + $function = new Function_Call_Reflector( $node ); - // Add the call to the list of functions used in this scope. - $this->getLocation()->uses['functions'][] = $function; + // Add the call to the list of functions used in this scope + $location = $this->getLocation(); + if ( ! isset( $location->uses ) ) { + $location->uses = array( 'functions' => array(), 'hooks' => array(), 'methods' => array() ); + } + if ( ! isset( $location->uses['functions'] ) ) { + $location->uses['functions'] = array(); + } + $location->uses['functions'][] = $function; if ( $this->isFilter( $node ) ) { if ( $this->last_doc && ! $node->getDocComment() ) { @@ -87,83 +233,98 @@ public function enterNode( \PHPParser_Node $node ) { $this->last_doc = null; } - $hook = new Hook_Reflector( $node, $this->context ); + $hook = new Hook_Reflector( $node ); - // Add it to the list of hooks used in this scope. - $this->getLocation()->uses['hooks'][] = $hook; + // Add it to the list of hooks used in this scope + $location = $this->getLocation(); + if ( ! isset( $location->uses ) ) { + $location->uses = array( 'functions' => array(), 'hooks' => array(), 'methods' => array() ); + } + if ( ! isset( $location->uses['hooks'] ) ) { + $location->uses['hooks'] = array(); + } + $location->uses['hooks'][] = $hook; } break; - // Parse out method calls, so we can export where methods are used. + // Parse out method calls case 'Expr_MethodCall': - $method = new Method_Call_Reflector( $node, $this->context ); - - // Add it to the list of methods used in this scope. - $this->getLocation()->uses['methods'][] = $method; + $method = new Method_Call_Reflector( $node ); + // Set class context for $this resolution + $current_class = $this->getCurrentClass(); + if ( $current_class ) { + $method->set_class( $current_class ); + } + $location = $this->getLocation(); + if ( ! isset( $location->uses ) ) { + $location->uses = array( 'functions' => array(), 'hooks' => array(), 'methods' => array() ); + } + if ( ! isset( $location->uses['methods'] ) ) { + $location->uses['methods'] = array(); + } + $location->uses['methods'][] = $method; break; - // Parse out method calls, so we can export where methods are used. + // Parse out static method calls case 'Expr_StaticCall': - $method = new Static_Method_Call_Reflector( $node, $this->context ); - - // Add it to the list of methods used in this scope. - $this->getLocation()->uses['methods'][] = $method; + $method = new Static_Method_Call_Reflector( $node ); + // Set class context for self/parent resolution + $current_class = $this->getCurrentClass(); + if ( $current_class ) { + $method->set_class( $current_class ); + } + $location = $this->getLocation(); + if ( ! isset( $location->uses ) ) { + $location->uses = array( 'functions' => array(), 'hooks' => array(), 'methods' => array() ); + } + if ( ! isset( $location->uses['methods'] ) ) { + $location->uses['methods'] = array(); + } + $location->uses['methods'][] = $method; break; - // Parse out `new Class()` calls as uses of Class::__construct(). + // Parse out `new Class()` calls as uses of Class::__construct() case 'Expr_New': - $method = new \WP_Parser\Method_Call_Reflector( $node, $this->context ); - - // Add it to the list of methods used in this scope. - $this->getLocation()->uses['methods'][] = $method; + $method = new Method_Call_Reflector( $node ); + // Set class context for $this resolution + $current_class = $this->getCurrentClass(); + if ( $current_class ) { + $method->set_class( $current_class ); + } + $location = $this->getLocation(); + if ( ! isset( $location->uses ) ) { + $location->uses = array( 'functions' => array(), 'hooks' => array(), 'methods' => array() ); + } + if ( ! isset( $location->uses['methods'] ) ) { + $location->uses['methods'] = array(); + } + $location->uses['methods'][] = $method; break; } - // 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() ) ) { + // Pick up DocBlock from non-documentable elements + if ( ! $this->isNodeDocumentable( $node ) && + 'Name' !== $node->getType() && + ( $docblock = $node->getDocComment() ) ) { $this->last_doc = $docblock; } + + return null; } /** - * Assign queued hooks to functions and update the node stack on leaving a node. - * - * 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. + * Called when leaving a node during traversal. * - * @param \PHPParser_Node $node + * @param Node $node The node being left. + * @return int|null */ - public function leaveNode( \PHPParser_Node $node ) { - - parent::leaveNode( $node ); - + public function leaveNode( Node $node ) { 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 ); - } - } - - $method->uses = $this->method_uses_queue[ $method->getName() ]; - } - } + // Process class and assign queued methods + $class_data = $this->processClass( $node ); + if ( $class_data !== null ) { + $this->classes[] = $class_data; } $this->method_uses_queue = array(); @@ -171,38 +332,276 @@ public function leaveNode( \PHPParser_Node $node ) { break; case 'Stmt_Function': - $function = array_pop( $this->location ); - if ( isset( $function->uses ) && ! empty( $function->uses ) ) { - end( $this->functions )->uses = $function->uses; + // Process function + $function_node = array_pop( $this->location ); + $function_data = $this->processFunction( $node ); + + if ( isset( $function_node->uses ) && ! empty( $function_node->uses ) ) { + $function_data['uses'] = $function_node->uses; } + + $this->functions[] = $function_data; break; case 'Stmt_ClassMethod': - $method = array_pop( $this->location ); + $method_node = array_pop( $this->location ); - /* - * 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; + // Store the list of elements used by this method in the queue + if ( ! empty( $method_node->uses ) ) { + $this->method_uses_queue[ $node->name->toString() ] = $method_node->uses; } break; } + + return null; + } + + /** + * Process a class node and extract class information. + * + * @param Node\Stmt\Class_ $node The class node. + * @return array Class data. + */ + protected function processClass( Node\Stmt\Class_ $node ) { + // Skip anonymous classes (where name is null) + if ( ! $node->name ) { + return null; + } + + $docblock = $this->parseDocComment( $node->getDocComment() ); + + return array( + 'name' => $node->name->toString(), + 'line' => $node->getStartLine(), + 'end_line' => $node->getEndLine(), + 'final' => $node->isFinal(), + 'abstract' => $node->isAbstract(), + 'extends' => $node->extends ? $node->extends->toString() : '', + 'implements' => $node->implements ? array_map( fn($impl) => $impl->toString(), $node->implements ) : array(), + 'docblock' => $docblock, + 'methods' => $this->processClassMethods( $node ), + 'properties' => $this->processClassProperties( $node ), + 'namespace' => $this->getCurrentNamespace(), + ); } /** - * @param \PHPParser_Node $node + * Process a function node and extract function information. * - * @return bool + * @param Node\Stmt\Function_ $node The function node. + * @return array Function data. */ - protected function isFilter( \PHPParser_Node $node ) { + protected function processFunction( Node\Stmt\Function_ $node ) { + $docblock = $this->parseDocComment( $node->getDocComment() ); + + return array( + 'name' => $node->name->toString(), + 'line' => $node->getStartLine(), + 'end_line' => $node->getEndLine(), + 'docblock' => $docblock, + 'namespace' => $this->getCurrentNamespace(), + 'parameters' => $this->processParameters( $node->params ), + ); + } + + /** + * Process class methods. + * + * @param Node\Stmt\Class_ $node The class node. + * @return array Methods data. + */ + protected function processClassMethods( Node\Stmt\Class_ $node ) { + $methods = array(); + + foreach ( $node->getMethods() as $method ) { + $docblock = $this->parseDocComment( $method->getDocComment() ); + $method_name = $method->name->toString(); + + $method_data = array( + 'name' => $method_name, + 'line' => $method->getStartLine(), + 'end_line' => $method->getEndLine(), + 'docblock' => $docblock, + 'visibility' => $this->getMethodVisibility( $method ), + 'static' => $method->isStatic(), + 'parameters' => $this->processParameters( $method->params ), + ); + + // Add queued uses for this method + if ( isset( $this->method_uses_queue[ $method_name ] ) ) { + $method_data['uses'] = $this->method_uses_queue[ $method_name ]; + } + + $methods[] = $method_data; + } + + return $methods; + } + + /** + * Process class properties. + * + * @param Node\Stmt\Class_ $node The class node. + * @return array Properties data. + */ + protected function processClassProperties( Node\Stmt\Class_ $node ) { + $properties = array(); + + foreach ( $node->stmts as $stmt ) { + if ( $stmt instanceof Node\Stmt\Property ) { + foreach ( $stmt->props as $prop ) { + $docblock = $this->parseDocComment( $stmt->getDocComment() ); + + $property_data = array( + 'name' => '$' . $prop->name->toString(), + 'line' => $stmt->getStartLine(), + 'end_line' => $stmt->getEndLine(), + 'docblock' => $docblock, + 'visibility' => $this->getPropertyVisibility( $stmt ), + 'static' => $stmt->isStatic(), + 'default' => $prop->default ? $this->pretty_printer->prettyPrintExpr( $prop->default ) : null, + ); + + $properties[] = $property_data; + } + } + } + + return $properties; + } + + /** + * Process function/method parameters. + * + * @param Node\Param[] $params Parameter nodes. + * @return array Parameters data. + */ + protected function processParameters( array $params ) { + $parameters = array(); + + foreach ( $params as $param ) { + $param_data = array( + 'name' => $param->var->name, + 'line' => $param->getStartLine(), + 'type' => $param->type ? $this->pretty_printer->prettyPrint( array( $param->type ) ) : null, + 'default' => $param->default ? $this->pretty_printer->prettyPrintExpr( $param->default ) : null, + ); + + $parameters[] = $param_data; + } + + return $parameters; + } + + /** + * Get method visibility. + * + * @param Node\Stmt\ClassMethod $method Method node. + * @return string Visibility (public, protected, private). + */ + protected function getMethodVisibility( Node\Stmt\ClassMethod $method ) { + if ( $method->isPrivate() ) { + return 'private'; + } + if ( $method->isProtected() ) { + return 'protected'; + } + return 'public'; + } + + /** + * Get property visibility. + * + * @param Node\Stmt\Property $property Property node. + * @return string Visibility (public, protected, private). + */ + protected function getPropertyVisibility( Node\Stmt\Property $property ) { + if ( $property->isPrivate() ) { + return 'private'; + } elseif ( $property->isProtected() ) { + return 'protected'; + } + + return 'public'; + } + + /** + * Parse a DocComment using phpstan/phpdoc-parser. + * + * @param Node\Comment\Doc|null $doc_comment DocComment node. + * @return array|null Parsed docblock data. + */ + protected function parseDocComment( $doc_comment ) { + if ( ! $doc_comment ) { + return null; + } + + try { + $config = new ParserConfig( usedAttributes: [] ); + $lexer = new Lexer( $config ); + $tokens = $lexer->tokenize( $doc_comment->getText() ); + $token_iterator = new TokenIterator( $tokens ); + $phpdoc_node = $this->phpdoc_parser->parse( $token_iterator ); + + return $this->convertPhpDocToArray( $phpdoc_node ); + } catch ( \Exception $e ) { + error_log( 'DocBlock parse error: ' . $e->getMessage() ); + return null; + } + } + + /** + * Convert PHPStan PhpDocNode to array format. + * + * @param PhpDocNode $phpdoc_node The parsed PHPDoc node. + * @return array Converted docblock data. + */ + protected function convertPhpDocToArray( PhpDocNode $phpdoc_node ) { + $docblock_data = array( + 'summary' => '', + 'description' => '', + 'tags' => array(), + ); + + // Extract summary and description from text nodes + $text_content = ''; + foreach ( $phpdoc_node->children as $child ) { + if ( $child instanceof \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode ) { + $text_content .= $child->text . "\n"; + } + } + + // Split into summary and description + $text_lines = array_filter( explode( "\n", trim( $text_content ) ) ); + if ( ! empty( $text_lines ) ) { + $docblock_data['summary'] = trim( $text_lines[0] ); + if ( count( $text_lines ) > 1 ) { + $docblock_data['description'] = trim( implode( "\n", array_slice( $text_lines, 1 ) ) ); + } + } + + // Extract tags + foreach ( $phpdoc_node->getTags() as $tag ) { + $tag_name = ltrim( $tag->name, '@' ); + $docblock_data['tags'][ $tag_name ][] = $tag->value ? (string) $tag->value : ''; + } + + return $docblock_data; + } + + /** + * Check if a function call node represents a WordPress filter/action. + * + * @param Node\Expr\FuncCall $node Function call node. + * @return bool True if it's a filter/action. + */ + protected function isFilter( Node\Expr\FuncCall $node ) { // Ignore variable functions - if ( 'Name' !== $node->name->getType() ) { + if ( ! $node->name instanceof Node\Name ) { return false; } - $calling = (string) $node->name; + $calling = $node->name->toString(); $functions = array( 'apply_filters', @@ -217,20 +616,50 @@ protected function isFilter( \PHPParser_Node $node ) { } /** - * @return File_Reflector + * Get the current location in the parsing stack. + * + * @return File_Reflector|Node Current location object. */ protected function getLocation() { return empty( $this->location ) ? $this : end( $this->location ); } /** - * @param \PHPParser_Node $node + * Check if a node is documentable (has meaningful documentation). + * + * @param Node $node The node to check. + * @return bool True if the node is documentable. + */ + protected function isNodeDocumentable( Node $node ) { + return $node instanceof Node\Stmt\Function_ + || $node instanceof Node\Stmt\Class_ + || $node instanceof Node\Stmt\ClassMethod + || $node instanceof Node\Stmt\Property + || $node instanceof Node\Stmt\ClassConst + || ( $node instanceof Node\Expr\FuncCall && $this->isFilter( $node ) ); + } + + /** + * Get the current namespace context. * - * @return bool + * @return string|null Current namespace or null if global scope. */ - protected function isNodeDocumentable( \PHPParser_Node $node ) { - return parent::isNodeDocumentable( $node ) - || ( $node instanceof \PHPParser_Node_Expr_FuncCall - && $this->isFilter( $node ) ); + protected function getCurrentNamespace() { + return $this->current_namespace; + } + + /** + * Get the current class context from the location stack. + * + * @return Node\Stmt\Class_|null Current class node or null if not in a class. + */ + protected function getCurrentClass() { + // Look through the location stack for the most recent class + for ( $i = count( $this->location ) - 1; $i >= 0; $i-- ) { + if ( $this->location[$i] instanceof Node\Stmt\Class_ ) { + return $this->location[$i]; + } + } + return null; } -} +} \ No newline at end of file diff --git a/lib/class-function-call-reflector.php b/lib/class-function-call-reflector.php index 233e6d8..c7c3ffb 100644 --- a/lib/class-function-call-reflector.php +++ b/lib/class-function-call-reflector.php @@ -1,51 +1,156 @@ node = $node; + $this->pretty_printer = new PrettyPrinter(); + } + + /** + * Get the function name. + * + * @return string Function name. */ public function getName() { - if ( isset( $this->node->namespacedName ) ) { - return '\\' . implode( '\\', $this->node->namespacedName->parts ); + // Handle direct name calls + if ( $this->node->name instanceof Node\Name ) { + return $this->node->name->toString(); + } + + // Handle variable function calls like $func() + if ( $this->node->name instanceof Node\Expr\Variable ) { + return '$' . $this->node->name->name; } - $shortName = $this->getShortName(); + // Handle array access like $callbacks['func']() + if ( $this->node->name instanceof Node\Expr\ArrayDimFetch ) { + $var_name = ''; + if ( $this->node->name->var instanceof Node\Expr\Variable ) { + $var_name = '$' . $this->node->name->var->name; + } - if ( is_a( $shortName, 'PHPParser_Node_Name_FullyQualified' ) ) { - return '\\' . (string) $shortName; + $dim_name = ''; + if ( $this->node->name->dim ) { + $dim_name = $this->pretty_printer->prettyPrintExpr( $this->node->name->dim ); + } + + return $var_name . '[' . $dim_name . ']'; } - if ( is_a( $shortName, 'PHPParser_Node_Name' ) ) { - return (string) $shortName; + // Handle property access like $obj->method() + if ( $this->node->name instanceof Node\Expr\PropertyFetch ) { + return $this->pretty_printer->prettyPrintExpr( $this->node->name ); } - /** @var \PHPParser_Node_Expr_ArrayDimFetch $shortName */ - if ( is_a( $shortName, 'PHPParser_Node_Expr_ArrayDimFetch' ) ) { - $var = $shortName->var->name; - $dim = $shortName->dim->name->parts[0]; + // Fallback to pretty printing the expression + return $this->pretty_printer->prettyPrintExpr( $this->node->name ); + } - return "\${$var}[{$dim}]"; + /** + * Get the short name (without namespace). + * + * @return string Short function name. + */ + public function getShortName() { + if ( $this->node->name instanceof Node\Name ) { + return $this->node->name->getLast(); } - /** @var \PHPParser_Node_Expr_Variable $shortName */ - if ( is_a( $shortName, 'PHPParser_Node_Expr_Variable' ) ) { - return $shortName->name; + return $this->getName(); + } + + /** + * Get the line number where the function call occurs. + * + * @return int Line number. + */ + public function getLine() { + return $this->node->getStartLine(); + } + + /** + * Get the function call arguments. + * + * @return array List of arguments. + */ + public function getArguments() { + $arguments = array(); + + foreach ( $this->node->args as $arg ) { + $arguments[] = $this->pretty_printer->prettyPrintExpr( $arg->value ); + } + + return $arguments; + } + + /** + * Check if this is a namespaced function call. + * + * @return bool True if namespaced. + */ + public function isNamespaced() { + return $this->node->name instanceof Node\Name && $this->node->name->isFullyQualified(); + } + + /** + * Get the namespace of the function call. + * + * @return string|null Namespace or null if not namespaced. + */ + public function getNamespace() { + if ( ! $this->node->name instanceof Node\Name ) { + return null; } - return (string) $shortName; + $parts = $this->node->name->parts; + if ( count( $parts ) <= 1 ) { + return null; + } + + return implode( '\\', array_slice( $parts, 0, -1 ) ); + } + + /** + * Convert function call to array format for export. + * + * @return array Function call data. + */ + public function toArray() { + return array( + 'name' => $this->getName(), + 'short_name' => $this->getShortName(), + 'line' => $this->getLine(), + 'arguments' => $this->getArguments(), + 'namespace' => $this->getNamespace(), + 'namespaced' => $this->isNamespaced(), + ); } -} +} \ No newline at end of file diff --git a/lib/class-hook-reflector.php b/lib/class-hook-reflector.php index 50075e0..ed00135 100644 --- a/lib/class-hook-reflector.php +++ b/lib/class-hook-reflector.php @@ -2,98 +2,168 @@ namespace WP_Parser; -use phpDocumentor\Reflection\BaseReflector; -use PHPParser_PrettyPrinter_Default; +use PhpParser\Node; +use PhpParser\PrettyPrinter\Standard as PrettyPrinter; /** - * Custom reflector for WordPress hooks. + * Modern reflector for WordPress hooks using PHPParser v5. */ -class Hook_Reflector extends BaseReflector { +class Hook_Reflector { /** - * @return string + * The function call node representing the hook. + * + * @var Node\Expr\FuncCall + */ + protected $node; + + /** + * Pretty printer for extracting hook names. + * + * @var PrettyPrinter + */ + protected $pretty_printer; + + /** + * Initialize the hook reflector. + * + * @param Node\Expr\FuncCall $node The function call node. + */ + public function __construct( Node\Expr\FuncCall $node ) { + $this->node = $node; + $this->pretty_printer = new PrettyPrinter(); + } + + /** + * Get the hook name. + * + * @return string The cleaned hook name. */ public function getName() { - $printer = new PHPParser_PrettyPrinter_Default; - return $this->cleanupName( $printer->prettyPrintExpr( $this->node->args[0]->value ) ); + if ( empty( $this->node->args ) ) { + return ''; + } + + $name_expr = $this->pretty_printer->prettyPrintExpr( $this->node->args[0]->value ); + return $this->cleanupName( $name_expr ); } /** - * @param string $name + * Get the hook type (action or filter). * - * @return string + * @return string 'action' or 'filter'. */ - private function cleanupName( $name ) { - $matches = array(); + public function getType() { + if ( ! $this->node->name instanceof Node\Name ) { + return 'unknown'; + } - // quotes on both ends of a string - if ( preg_match( '/^[\'"]([^\'"]*)[\'"]$/', $name, $matches ) ) { - return $matches[1]; + $function_name = $this->node->name->toString(); + + $filter_functions = array( + 'apply_filters', + 'apply_filters_ref_array', + 'apply_filters_deprecated', + ); + + $action_functions = array( + 'do_action', + 'do_action_ref_array', + 'do_action_deprecated', + ); + + if ( in_array( $function_name, $filter_functions ) ) { + return 'filter'; } - // two concatenated things, last one of them a variable - if ( preg_match( - '/(?:[\'"]([^\'"]*)[\'"]\s*\.\s*)?' . // First filter name string (optional) - '(\$[^\s]*)' . // Dynamic variable - '(?:\s*\.\s*[\'"]([^\'"]*)[\'"])?/', // Second filter name string (optional) - $name, $matches ) ) { - - if ( isset( $matches[3] ) ) { - return $matches[1] . '{' . $matches[2] . '}' . $matches[3]; - } else { - return $matches[1] . '{' . $matches[2] . '}'; - } + if ( in_array( $function_name, $action_functions ) ) { + return 'action'; } - return $name; + return 'unknown'; } /** - * @return string + * Get the line number where the hook is defined. + * + * @return int Line number. */ - public function getShortName() { - return $this->getName(); + public function getLine() { + return $this->node->getStartLine(); } /** - * @return string + * Get the hook arguments. + * + * @return array List of hook arguments. */ - public function getType() { - $type = 'filter'; - switch ( (string) $this->node->name ) { - case 'do_action': - $type = 'action'; - break; - case 'do_action_ref_array': - $type = 'action_reference'; - break; - case 'do_action_deprecated': - $type = 'action_deprecated'; - break; - case 'apply_filters_ref_array': - $type = 'filter_reference'; - break; - case 'apply_filters_deprecated'; - $type = 'filter_deprecated'; - break; + public function getArguments() { + $arguments = array(); + + // Skip the first argument (hook name) and process the rest + $args = array_slice( $this->node->args, 1 ); + + foreach ( $args as $index => $arg ) { + $arguments[] = $this->pretty_printer->prettyPrintExpr( $arg->value ); } - return $type; + return $arguments; } /** - * @return array + * Get the docblock associated with this hook. + * + * @return string|null DocBlock text or null if none. + */ + public function getDocComment() { + $doc_comment = $this->node->getDocComment(); + return $doc_comment ? $doc_comment->getText() : null; + } + + /** + * Clean up hook name by handling variables and concatenation. + * + * @param string $name Raw hook name expression. + * @return string Cleaned hook name. */ - public function getArgs() { - $printer = new Pretty_Printer; - $args = array(); - foreach ( $this->node->args as $arg ) { - $args[] = $printer->prettyPrintArg( $arg ); + private function cleanupName( $name ) { + // Remove quotes from simple strings + if ( preg_match( '/^[\'"]([^\'"]*)[\'"]$/', $name, $matches ) ) { + return $matches[1]; + } + + // Handle concatenated strings with variables + // Pattern: 'string' . $variable . 'string' + if ( preg_match( + '/(?:[\'"]([^\'"]*)[\'"]\s*\.\s*)?' . // First string part (optional) + '(\$[^\s]*)' . // Variable part + '(?:\s*\.\s*[\'"]([^\'"]*)[\'"])?/', // Second string part (optional) + $name, + $matches + ) ) { + $first_part = isset( $matches[1] ) ? $matches[1] : ''; + $variable = $matches[2]; + $last_part = isset( $matches[3] ) ? $matches[3] : ''; + + return $first_part . '{' . $variable . '}' . $last_part; } - // Skip the filter name - array_shift( $args ); + // Return as-is if we can't parse it + return $name; + } - return $args; + /** + * Convert hook to array format for export. + * + * @return array Hook data array. + */ + public function toArray() { + return array( + 'name' => $this->getName(), + 'type' => $this->getType(), + 'line' => $this->getLine(), + 'arguments' => $this->getArguments(), + 'doc_comment' => $this->getDocComment(), + ); } -} +} \ No newline at end of file diff --git a/lib/class-importer.php b/lib/class-importer.php index bc72372..b83e212 100644 --- a/lib/class-importer.php +++ b/lib/class-importer.php @@ -303,7 +303,7 @@ public function import_file( array $file, $skip_sleep = false, $import_ignored = if ( '_deprecated_file' === $first_function['name'] ) { // Set the deprecated flag to the version number - $deprecated_file = $first_function['deprecation_version']; + $deprecated_file = $first_function['deprecation_version'] ?? ''; } } @@ -431,10 +431,10 @@ protected function import_class( array $data, $import_ignored = false ) { } // Set class-specific meta - update_post_meta( $class_id, '_wp-parser_final', (string) $data['final'] ); - update_post_meta( $class_id, '_wp-parser_abstract', (string) $data['abstract'] ); - update_post_meta( $class_id, '_wp-parser_extends', $data['extends'] ); - update_post_meta( $class_id, '_wp-parser_implements', $data['implements'] ); + update_post_meta( $class_id, '_wp-parser_final', (string) ( $data['final'] ?? false ) ); + update_post_meta( $class_id, '_wp-parser_abstract', (string) ( $data['abstract'] ?? false ) ); + update_post_meta( $class_id, '_wp-parser_extends', $data['extends'] ?? '' ); + update_post_meta( $class_id, '_wp-parser_implements', $data['implements'] ?? array() ); update_post_meta( $class_id, '_wp-parser_properties', $data['properties'] ); // Now add the methods @@ -758,7 +758,7 @@ public function import_item( array $data, $parent_post_id = 0, $import_ignored = } $anything_updated[] = update_post_meta( $post_id, '_wp-parser_line_num', (string) $data['line'] ); - $anything_updated[] = update_post_meta( $post_id, '_wp-parser_end_line_num', (string) $data['end_line'] ); + $anything_updated[] = update_post_meta( $post_id, '_wp-parser_end_line_num', (string) ( $data['end_line'] ?? $data['line'] ) ); $anything_updated[] = update_post_meta( $post_id, '_wp-parser_tags', $data['doc']['tags'] ); $anything_updated[] = update_post_meta( $post_id, '_wp-parser_last_parsed_wp_version', $this->version ); diff --git a/lib/class-method-call-reflector.php b/lib/class-method-call-reflector.php index 570d5a5..0e67e7a 100644 --- a/lib/class-method-call-reflector.php +++ b/lib/class-method-call-reflector.php @@ -2,157 +2,185 @@ namespace WP_Parser; -use phpDocumentor\Reflection\BaseReflector; -use phpDocumentor\Reflection\ClassReflector; +use PhpParser\Node; +use PhpParser\PrettyPrinter\Standard as PrettyPrinter; /** - * A reflection of a method call expression. + * Modern reflector for method calls using PHPParser v5. */ -class Method_Call_Reflector extends BaseReflector { +class Method_Call_Reflector { /** - * The class that this method was called in, if it was called in a class. + * The method call node. * - * @var ClassReflector|false + * @var Node\Expr\MethodCall|Node\Expr\New_ */ - protected $called_in_class = false; + protected $node; /** - * Returns the name for this Reflector instance. + * Pretty printer for extracting names. * - * @return string[] Index 0 is the calling instance, 1 is the method name. + * @var PrettyPrinter + */ + protected $pretty_printer; + + /** + * The class context if available. + * + * @var object|null + */ + protected $class_context; + + /** + * Initialize the method call reflector. + * + * @param Node\Expr\MethodCall|Node\Expr\New_ $node The method call or new node. + */ + public function __construct( $node ) { + $this->node = $node; + $this->pretty_printer = new PrettyPrinter(); + } + + /** + * Get the method name. + * + * @return string Method name. */ public function getName() { + // Handle constructor calls (new Class()) + if ( $this->node instanceof Node\Expr\New_ ) { + return '__construct'; + } - if ( 'Expr_New' === $this->node->getType() ) { - $name = '__construct'; - $caller = $this->node->class; - } else { - $name = $this->getShortName(); - $caller = $this->node->var; + // Handle regular method calls + if ( $this->node instanceof Node\Expr\MethodCall ) { + if ( $this->node->name instanceof Node\Identifier ) { + return $this->node->name->toString(); + } + + // Handle variable method calls like $obj->$method() + return $this->pretty_printer->prettyPrintExpr( $this->node->name ); } - 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 ''; + } + + /** + * Get the class name being called. + * + * @return string Class name. + */ + public function getClass() { + // Handle constructor calls (new Class()) + if ( $this->node instanceof Node\Expr\New_ ) { + if ( $this->node->class instanceof Node\Name ) { + $class_name = $this->node->class->toString(); + + // Resolve self and parent to actual class names + if ( 'self' === $class_name && $this->class_context ) { + $class_name = $this->class_context->name->toString(); + } elseif ( 'parent' === $class_name && $this->class_context && $this->class_context->extends ) { + $class_name = $this->class_context->extends->toString(); + } + + // Add leading backslash for fully qualified class names + return $class_name && ! str_starts_with( $class_name, '\\' ) ? '\\' . $class_name : $class_name; + } + + // Handle variable class instantiation like new $class() + if ( $this->node->class instanceof Node\Expr ) { + return $this->pretty_printer->prettyPrintExpr( $this->node->class ); + } + + // For anonymous classes or unsupported cases, return null + return null; } - $caller = $this->_resolveName( $caller ); + // Handle regular method calls + if ( $this->node instanceof Node\Expr\MethodCall ) { + // Try to determine class from variable + if ( $this->node->var instanceof Node\Expr\Variable ) { + $var_name = $this->node->var->name; - // If the caller is a function, convert it to the function name - if ( is_a( $caller, 'PHPParser_Node_Expr_FuncCall' ) ) { + // Handle common patterns + if ( '$this' === '$' . $var_name && $this->class_context ) { + $class_name = $this->class_context->name; + // Add leading backslash for fully qualified class names + return $class_name && ! str_starts_with( $class_name, '\\' ) ? '\\' . $class_name : $class_name; + } - // Add parentheses to signify this is a function call - /** @var \PHPParser_Node_Expr_FuncCall $caller */ - $caller = implode( '\\', $caller->name->parts ) . '()'; - } + return '$' . $var_name; + } - $class_mapping = $this->_getClassMapping(); - if ( array_key_exists( $caller, $class_mapping ) ) { - $caller = $class_mapping[ $caller ]; + // Handle chained calls like $obj->method()->anotherMethod() + return $this->pretty_printer->prettyPrintExpr( $this->node->var ); } - return array( $caller, $name ); + return ''; } /** - * Set the class that this method was called within. + * Get the line number where the method call occurs. * - * @param ClassReflector $class + * @return int Line number. */ - public function set_class( ClassReflector $class ) { + public function getLine() { + return $this->node->getStartLine(); + } + + /** + * Get the method call arguments. + * + * @return array List of arguments. + */ + public function getArguments() { + $arguments = array(); + + $args = array(); + if ( $this->node instanceof Node\Expr\MethodCall ) { + $args = $this->node->args; + } elseif ( $this->node instanceof Node\Expr\New_ ) { + $args = $this->node->args ?? array(); + } - $this->called_in_class = $class; + foreach ( $args as $arg ) { + $arguments[] = $this->pretty_printer->prettyPrintExpr( $arg->value ); + } + + return $arguments; } /** - * Returns whether or not this method call is a static call + * Check if this is a static method call. * - * @return bool Whether or not this method call is a static call + * @return bool False for instance method calls, true for static. */ public function isStatic() { + // Constructor calls and method calls are not static return false; } /** - * Returns a mapping from variable names to a class name, leverages globals for most used classes + * Set the class context for resolving $this references. * - * @return array Class mapping to map variable names to classes + * @param object $class_context The class context. */ - 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', - ); - - $class_mapping = array_merge( $wp_globals, $wp_functions ); - - return $class_mapping; + public function set_class( $class_context ) { + $this->class_context = $class_context; } /** - * Resolve a class name from self/parent. - * - * @param string $class The class name. + * Convert method call to array format for export. * - * @return string The resolved class name. + * @return array Method call data. */ - protected function _resolveName( $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; - case 'parent': - $class = '\\' . $this->called_in_class->getNode()->extends->toString(); - break; - } - - return $class; + public function toArray() { + return array( + 'name' => $this->getName(), + 'class' => $this->getClass(), + 'line' => $this->getLine(), + 'arguments' => $this->getArguments(), + 'static' => $this->isStatic(), + ); } -} +} \ No newline at end of file diff --git a/lib/class-relationships.php b/lib/class-relationships.php index ad29e18..132df3e 100644 --- a/lib/class-relationships.php +++ b/lib/class-relationships.php @@ -49,14 +49,19 @@ public function __construct() { } /** - * Load the posts2posts from the composer package if it is not loaded already. + * Load the posts2posts from the WordPress plugin if available. */ public function require_posts_to_posts() { - // Initializes the database tables - \P2P_Storage::init(); + // Only initialize if P2P classes exist (from WordPress plugin) + if ( class_exists( 'P2P_Storage' ) ) { + // Initializes the database tables + \P2P_Storage::init(); + } - // Initializes the query mechanism - \P2P_Query_Post::init(); + if ( class_exists( 'P2P_Query_Post' ) ) { + // Initializes the query mechanism + \P2P_Query_Post::init(); + } } /** @@ -69,6 +74,10 @@ public function require_posts_to_posts() { * @link https://github.com/scribu/wp-posts-to-posts/wiki/p2p_register_connection_type */ public function register_post_relationships() { + // Only register relationships if P2P functions are available + if ( ! function_exists( 'p2p_register_connection_type' ) ) { + return; + } /* * Functions to functions, methods and hooks @@ -128,7 +137,7 @@ public function register_post_relationships() { public function wp_parser_starting_import() { $importer = new Importer; - if ( ! $this->p2p_tables_exist() ) { + if ( ! $this->p2p_tables_exist() && class_exists( 'P2P_Storage' ) ) { \P2P_Storage::init(); \P2P_Storage::install(); } @@ -176,7 +185,7 @@ public function import_item( $post_id, $data, $post_data ) { // Functions to Functions $to_type = $this->post_types['function']; foreach ( (array) @$data['uses']['functions'] as $to_function ) { - $to_function_slug = $this->names_to_slugs( $to_function['name'], $data['namespace'] ); + $to_function_slug = $this->names_to_slugs( $to_function['name'], $data['namespace'] ?? '' ); $this->relationships[ $from_type ][ $post_id ][ $to_type ][] = $to_function_slug; } @@ -190,7 +199,7 @@ public function import_item( $post_id, $data, $post_data ) { } else { $to_method_slug = $to_method['name']; } - $to_method_slug = $this->names_to_slugs( $to_method_slug, $data['namespace'] ); + $to_method_slug = $this->names_to_slugs( $to_method_slug, $data['namespace'] ?? '' ); $this->relationships[ $from_type ][ $post_id ][ $to_type ][] = $to_method_slug; } @@ -210,7 +219,7 @@ public function import_item( $post_id, $data, $post_data ) { // Methods to Functions $to_type = $this->post_types['function']; foreach ( (array) @$data['uses']['functions'] as $to_function ) { - $to_function_slug = $this->names_to_slugs( $to_function['name'], $data['namespace'] ); + $to_function_slug = $this->names_to_slugs( $to_function['name'], $data['namespace'] ?? '' ); $this->relationships[ $from_type ][ $post_id ][ $to_type ][] = $to_function_slug; } @@ -228,7 +237,7 @@ public function import_item( $post_id, $data, $post_data ) { } else { $to_method_slug = $to_method['name']; } - $to_method_slug = $this->names_to_slugs( $to_method_slug, $data['namespace'] ); + $to_method_slug = $this->names_to_slugs( $to_method_slug, $data['namespace'] ?? '' ); $this->relationships[ $from_type ][ $post_id ][ $to_type ][] = $to_method_slug; } @@ -249,6 +258,11 @@ public function import_item( $post_id, $data, $post_data ) { */ public function wp_parser_ending_import() { + // Only process relationships if P2P functions are available + if ( ! function_exists( 'p2p_delete_connections' ) || ! function_exists( 'p2p_type' ) ) { + return; + } + if ( defined( 'WP_CLI' ) && WP_CLI ) { WP_CLI::log( 'Removing current relationships...' ); } diff --git a/lib/class-static-method-call-reflector.php b/lib/class-static-method-call-reflector.php index 9d39c7c..182a8f0 100644 --- a/lib/class-static-method-call-reflector.php +++ b/lib/class-static-method-call-reflector.php @@ -2,28 +2,176 @@ namespace WP_Parser; +use PhpParser\Node; +use PhpParser\PrettyPrinter\Standard as PrettyPrinter; + /** - * A reflection of a method call expression. + * Modern reflector for static method calls using PHPParser v5. */ -class Static_Method_Call_Reflector extends Method_Call_Reflector { +class Static_Method_Call_Reflector { + + /** + * The static method call node. + * + * @var Node\Expr\StaticCall + */ + protected $node; + + /** + * Pretty printer for extracting names. + * + * @var PrettyPrinter + */ + protected $pretty_printer; + + /** + * The class context if available. + * + * @var object|null + */ + protected $class_context; + + /** + * Initialize the static method call reflector. + * + * @param Node\Expr\StaticCall $node The static method call node. + */ + public function __construct( Node\Expr\StaticCall $node ) { + $this->node = $node; + $this->pretty_printer = new PrettyPrinter(); + } /** - * Returns the name for this Reflector instance. + * Get the method name. * - * @return string[] Index 0 is the class name, 1 is the method name. + * @return string Method name. */ public function getName() { - $class = $this->node->class; - $prefix = ( is_a( $class, 'PHPParser_Node_Name_FullyQualified' ) ) ? '\\' : ''; - $class = $prefix . $this->_resolveName( implode( '\\', $class->parts ) ); + if ( $this->node->name instanceof Node\Identifier ) { + return $this->node->name->toString(); + } + + // Handle variable method calls like Class::$method() + return $this->pretty_printer->prettyPrintExpr( $this->node->name ); + } + + /** + * Get the class name being called. + * + * @return string Class name. + */ + public function getClass() { + if ( $this->node->class instanceof Node\Name ) { + $class_name = $this->node->class->toString(); + + // Resolve self and parent to actual class names + if ( 'self' === $class_name && $this->class_context ) { + $class_name = $this->class_context->name->toString(); + } elseif ( 'parent' === $class_name && $this->class_context && $this->class_context->extends ) { + $class_name = $this->class_context->extends->toString(); + } + + // Add leading backslash for fully qualified class names + return $class_name && ! str_starts_with( $class_name, '\\' ) ? '\\' . $class_name : $class_name; + } + + // Handle variable class calls like $class::method() + return $this->pretty_printer->prettyPrintExpr( $this->node->class ); + } + + /** + * Get the full method signature (Class::method). + * + * @return string Full method signature. + */ + public function getFullName() { + return $this->getClass() . '::' . $this->getName(); + } + + /** + * Get the line number where the static method call occurs. + * + * @return int Line number. + */ + public function getLine() { + return $this->node->getStartLine(); + } + + /** + * Get the method call arguments. + * + * @return array List of arguments. + */ + public function getArguments() { + $arguments = array(); - return array( $class, $this->getShortName() ); + foreach ( $this->node->args as $arg ) { + $arguments[] = $this->pretty_printer->prettyPrintExpr( $arg->value ); + } + + return $arguments; } /** - * @return bool + * Check if this is a static method call. + * + * @return bool Always true for static method calls. */ public function isStatic() { return true; } -} + + /** + * Set the class context for resolving self/parent references. + * + * @param object $class_context The class context. + */ + public function set_class( $class_context ) { + $this->class_context = $class_context; + } + + /** + * Check if the class is fully qualified. + * + * @return bool True if fully qualified. + */ + public function isFullyQualified() { + return $this->node->class instanceof Node\Name && $this->node->class->isFullyQualified(); + } + + /** + * Get the namespace of the class. + * + * @return string|null Namespace or null if not namespaced. + */ + public function getNamespace() { + if ( ! $this->node->class instanceof Node\Name ) { + return null; + } + + $parts = $this->node->class->parts; + if ( count( $parts ) <= 1 ) { + return null; + } + + return implode( '\\', array_slice( $parts, 0, -1 ) ); + } + + /** + * Convert static method call to array format for export. + * + * @return array Static method call data. + */ + public function toArray() { + return array( + 'name' => $this->getName(), + 'class' => $this->getClass(), + 'full_name' => $this->getFullName(), + 'line' => $this->getLine(), + 'arguments' => $this->getArguments(), + 'static' => $this->isStatic(), + 'namespace' => $this->getNamespace(), + 'fully_qualified' => $this->isFullyQualified(), + ); + } +} \ No newline at end of file diff --git a/lib/class-wp-cli-logger.php b/lib/class-wp-cli-logger.php index cba60a0..49fe584 100644 --- a/lib/class-wp-cli-logger.php +++ b/lib/class-wp-cli-logger.php @@ -11,12 +11,13 @@ class WP_CLI_Logger extends AbstractLogger { /** * @param string $level - * @param string $message + * @param \Stringable|string $message * @param array $context * * @return void */ - public function log( $level, $message, array $context = array() ) { + public function log( $level, \Stringable|string $message, array $context = array() ): void { + $message = (string) $message; switch ( $level ) { diff --git a/lib/runner.php b/lib/runner.php index ba3efdd..9801315 100644 --- a/lib/runner.php +++ b/lib/runner.php @@ -2,23 +2,17 @@ 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 + * Get all PHP files from a directory recursively. * - * @return array|\WP_Error + * @param string $directory Directory to scan. + * @return array|\WP_Error Array of file paths or WP_Error on failure. */ function get_wp_files( $directory ) { $iterableFiles = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $directory ) ); - $files = array(); + $files = array(); try { foreach ( $iterableFiles as $file ) { @@ -39,92 +33,135 @@ function get_wp_files( $directory ) { } /** - * @param array $files - * @param string $root + * Parse PHP files using the modernized parser. * - * @return array + * @param array $files Array of file paths to parse. + * @param string $root Root directory path. + * @return array Parsed data in legacy format for compatibility. */ function parse_files( $files, $root ) { $output = array(); foreach ( $files as $filename ) { - $file = new File_Reflector( $filename ); + $content = file_get_contents( $filename ); + if ( false === $content ) { + continue; + } - $path = ltrim( substr( $filename, strlen( $root ) ), DIRECTORY_SEPARATOR ); - $file->setFilename( $path ); + $file_reflector = new File_Reflector( $filename, $content ); + $parsed_data = $file_reflector->parse(); - $file->process(); + $path = ltrim( substr( $filename, strlen( $root ) ), DIRECTORY_SEPARATOR ); - // TODO proper exporter + // Convert to legacy format expected by tests $out = array( - 'file' => export_docblock( $file ), - 'path' => str_replace( DIRECTORY_SEPARATOR, '/', $file->getFilename() ), + 'path' => str_replace( DIRECTORY_SEPARATOR, '/', $path ), 'root' => $root, + 'file' => export_docblock_from_data( $parsed_data['file_docblock'] ), ); - if ( ! empty( $file->uses ) ) { - $out['uses'] = export_uses( $file->uses ); + // Add file-level uses (hooks, functions, methods) + if ( ! empty( $parsed_data['uses'] ) ) { + $out['uses'] = export_uses( $parsed_data['uses'] ); } - foreach ( $file->getIncludes() as $include ) { - $out['includes'][] = array( - 'name' => $include->getName(), - 'line' => $include->getLineNumber(), - 'type' => $include->getType(), - ); + // Convert hooks to legacy format + if ( ! empty( $parsed_data['uses']['hooks'] ) ) { + $out['hooks'] = export_hooks( $parsed_data['uses']['hooks'] ); } - foreach ( $file->getConstants() as $constant ) { - $out['constants'][] = array( - 'name' => $constant->getShortName(), - 'line' => $constant->getLineNumber(), - 'value' => $constant->getValue(), - ); - } + // Convert functions to legacy format + if ( ! empty( $parsed_data['functions'] ) ) { + $out['functions'] = array(); + foreach ( $parsed_data['functions'] as $function ) { + $func = array( + 'name' => $function['name'], + 'namespace' => $function['namespace'], + 'line' => $function['line'], + 'end_line' => $function['end_line'], + 'arguments' => export_arguments( $function['parameters'] ?? array() ), + 'doc' => export_docblock_from_data( $function['docblock'] ), + 'hooks' => array(), + ); + + // Add function-level uses + if ( ! empty( $function['uses'] ) ) { + $func['uses'] = export_uses( $function['uses'] ); + + // Extract hooks from function uses + if ( ! empty( $function['uses']['hooks'] ) ) { + $func['hooks'] = export_hooks( $function['uses']['hooks'] ); + } + } - if ( ! empty( $file->uses['hooks'] ) ) { - $out['hooks'] = export_hooks( $file->uses['hooks'] ); + $out['functions'][] = $func; + } } - foreach ( $file->getFunctions() as $function ) { - $func = array( - 'name' => $function->getShortName(), - 'namespace' => $function->getNamespace(), - 'aliases' => $function->getNamespaceAliases(), - 'line' => $function->getLineNumber(), - 'end_line' => $function->getNode()->getAttribute( 'endLine' ), - 'arguments' => export_arguments( $function->getArguments() ), - 'doc' => export_docblock( $function ), - 'hooks' => array(), - ); - - if ( ! empty( $function->uses ) ) { - $func['uses'] = export_uses( $function->uses ); - - if ( ! empty( $function->uses['hooks'] ) ) { - $func['hooks'] = export_hooks( $function->uses['hooks'] ); + // Convert classes to legacy format + if ( ! empty( $parsed_data['classes'] ) ) { + $out['classes'] = array(); + foreach ( $parsed_data['classes'] as $class ) { + $class_data = array( + 'name' => $class['name'], + 'namespace' => $class['namespace'], + 'line' => $class['line'], + 'end_line' => $class['end_line'], + 'doc' => export_docblock_from_data( $class['docblock'] ), + 'uses' => array(), + 'methods' => array(), + 'properties' => array(), + ); + + // Convert methods + if ( ! empty( $class['methods'] ) ) { + foreach ( $class['methods'] as $method ) { + $method_data = array( + 'name' => $method['name'], + 'line' => $method['line'], + 'end_line' => $method['end_line'], + 'arguments' => export_arguments( $method['parameters'] ?? array() ), + 'doc' => export_docblock_from_data( $method['docblock'] ), + 'visibility' => $method['visibility'], + 'final' => false, // Would need to be added to parser + 'static' => $method['static'], + 'abstract' => false, // Would need to be added to parser + 'hooks' => array(), + ); + + // Add method-level uses + if ( ! empty( $method['uses'] ) ) { + $method_data['uses'] = export_uses( $method['uses'] ); + + // Extract hooks from method uses + if ( ! empty( $method['uses']['hooks'] ) ) { + $method_data['hooks'] = export_hooks( $method['uses']['hooks'] ); + } + } + + $class_data['methods'][] = $method_data; + } } - } - $out['functions'][] = $func; - } + // Convert properties + if ( ! empty( $class['properties'] ) ) { + foreach ( $class['properties'] as $property ) { + $property_data = array( + 'name' => $property['name'], + 'line' => $property['line'], + 'end_line' => $property['end_line'], + 'doc' => export_docblock_from_data( $property['docblock'] ), + 'visibility' => $property['visibility'], + 'static' => $property['static'], + 'default' => $property['default'], + ); + + $class_data['properties'][] = $property_data; + } + } - foreach ( $file->getClasses() as $class ) { - $class_data = array( - 'name' => $class->getShortName(), - 'namespace' => $class->getNamespace(), - 'line' => $class->getLineNumber(), - 'end_line' => $class->getNode()->getAttribute( 'endLine' ), - 'final' => $class->isFinal(), - 'abstract' => $class->isAbstract(), - 'extends' => $class->getParentClass(), - 'implements' => $class->getInterfaces(), - 'properties' => export_properties( $class->getProperties() ), - 'methods' => export_methods( $class->getMethods() ), - 'doc' => export_docblock( $class ), - ); - - $out['classes'][] = $class_data; + $out['classes'][] = $class_data; + } } $output[] = $out; @@ -134,294 +171,249 @@ function parse_files( $files, $root ) { } /** - * Fixes newline handling in parsed text. - * - * DocBlock lines, particularly for descriptions, generally adhere to a given character width. For sentences and - * paragraphs that exceed that width, what is intended as a manual soft wrap (via line break) is used to ensure - * on-screen/in-file legibility of that text. These line breaks are retained by phpDocumentor. However, consumers - * of this parsed data may believe the line breaks to be intentional and may display the text as such. - * - * This function fixes text by merging consecutive lines of text into a single line. A special exception is made - * for text appearing in `` and `
` tags, as newlines appearing in those tags are always intentional.
+ * Export uses data to legacy format.
  *
- * @param string $text
- *
- * @return string
+ * @param array $uses Uses data from modern parser.
+ * @return array Legacy format uses.
  */
-function fix_newlines( $text ) {
-	// Non-naturally occurring string to use as temporary replacement.
-	$replacement_string = '{{{{{}}}}}';
-
-	// Replace newline characters within 'code' and 'pre' tags with replacement string.
-	$text = preg_replace_callback(
-		"/(
]*>)(.+)(?=<\/code><\/pre>)/sU",
-		function ( $matches ) use ( $replacement_string ) {
-			return preg_replace( '/[\n\r]/', $replacement_string, $matches[1] . $matches[2] );
-		},
-		$text
-	);
+function export_uses( $uses ) {
+	$exported = array();
 
-	// Insert a newline when \n follows `.`.
-	$text = preg_replace(
-		"/\.[\n\r]+(?!\s*[\n\r])/m",
-		'.
', - $text - ); - - // Insert a new line when \n is followed by what appears to be a list. - $text = preg_replace( - "/[\n\r]+(\s+[*-] )(?!\s*[\n\r])/m", - '
$1', - $text - ); + if ( ! empty( $uses['functions'] ) ) { + $exported['functions'] = array(); + foreach ( $uses['functions'] as $function ) { + $exported['functions'][] = export_function_call( $function ); + } + } - // Merge consecutive non-blank lines together by replacing the newlines with a space. - $text = preg_replace( - "/[\n\r](?!\s*[\n\r])/m", - ' ', - $text - ); + if ( ! empty( $uses['methods'] ) ) { + $exported['methods'] = array(); + foreach ( $uses['methods'] as $method ) { + $exported['methods'][] = export_method_call( $method ); + } + } - // Restore newline characters into code blocks. - $text = str_replace( $replacement_string, "\n", $text ); + if ( ! empty( $uses['hooks'] ) ) { + $exported['hooks'] = array(); + foreach ( $uses['hooks'] as $hook ) { + $exported['hooks'][] = export_hook( $hook ); + } + } - return $text; + return $exported; } /** - * @param BaseReflector|ReflectionAbstract $element + * Export hooks to legacy format. * - * @return array + * @param array $hooks Hooks data. + * @return array Legacy format hooks. */ -function export_docblock( $element ) { - $docblock = $element->getDocBlock(); - if ( ! $docblock ) { - return array( - 'description' => '', - 'long_description' => '', - 'tags' => array(), - ); +function export_hooks( $hooks ) { + $exported = array(); + + foreach ( $hooks as $hook ) { + $exported[] = export_hook( $hook ); } - $output = array( - 'description' => preg_replace( '/[\n\r]+/', ' ', $docblock->getShortDescription() ), - 'long_description' => fix_newlines( $docblock->getLongDescription()->getFormattedContents() ), - 'tags' => array(), + return $exported; +} + +/** + * Export a single hook to legacy format. + * + * @param Hook_Reflector $hook Hook reflector instance. + * @return array Legacy format hook data. + */ +function export_hook( $hook ) { + $doc_comment = $hook->getDocComment(); + $doc = array( + 'description' => '', + 'long_description' => '', + 'tags' => array(), ); - foreach ( $docblock->getTags() as $tag ) { - $tag_data = array( - 'name' => $tag->getName(), - 'content' => preg_replace( '/[\n\r]+/', ' ', format_description( $tag->getDescription() ) ), - ); - if ( method_exists( $tag, 'getTypes' ) ) { - $tag_data['types'] = $tag->getTypes(); - } - if ( method_exists( $tag, 'getLink' ) ) { - $tag_data['link'] = $tag->getLink(); - } - if ( method_exists( $tag, 'getVariableName' ) ) { - $tag_data['variable'] = $tag->getVariableName(); - } - if ( method_exists( $tag, 'getReference' ) ) { - $tag_data['refers'] = $tag->getReference(); - } - if ( method_exists( $tag, 'getVersion' ) ) { - // Version string. - $version = $tag->getVersion(); - if ( ! empty( $version ) ) { - $tag_data['content'] = $version; - } - // Description string. - if ( method_exists( $tag, 'getDescription' ) ) { - $description = preg_replace( '/[\n\r]+/', ' ', format_description( $tag->getDescription() ) ); - if ( ! empty( $description ) ) { - $tag_data['description'] = $description; - } + if ( $doc_comment ) { + // Parse basic doc comment for hooks + $lines = explode( "\n", trim( str_replace( array( '/**', '*/', '*' ), '', $doc_comment ) ) ); + $description_lines = array(); + foreach ( $lines as $line ) { + $line = trim( $line ); + if ( $line && ! str_starts_with( $line, '@' ) ) { + $description_lines[] = $line; } } - $output['tags'][] = $tag_data; + if ( ! empty( $description_lines ) ) { + $doc['description'] = implode( ' ', $description_lines ); + } } - return $output; + return array( + 'name' => $hook->getName(), + 'line' => $hook->getLine(), + 'end_line' => $hook->getLine(), + 'type' => $hook->getType(), + 'arguments' => $hook->getArguments(), + 'doc' => $doc, + ); } /** - * @param Hook_Reflector[] $hooks + * Export function call to legacy format. * - * @return array + * @param Function_Call_Reflector $function Function call reflector. + * @return array Legacy format function call data. */ -function export_hooks( array $hooks ) { - $out = array(); - - foreach ( $hooks as $hook ) { - $out[] = array( - 'name' => $hook->getName(), - 'line' => $hook->getLineNumber(), - 'end_line' => $hook->getNode()->getAttribute( 'endLine' ), - 'type' => $hook->getType(), - 'arguments' => $hook->getArgs(), - 'doc' => export_docblock( $hook ), - ); - } - - return $out; +function export_function_call( $function ) { + return array( + 'name' => $function->getName(), + 'line' => $function->getLine(), + 'end_line' => $function->getLine(), + ); } /** - * @param ArgumentReflector[] $arguments + * Export method call to legacy format. * - * @return array + * @param Method_Call_Reflector|Static_Method_Call_Reflector $method Method call reflector. + * @return array Legacy format method call data. */ -function export_arguments( array $arguments ) { - $output = array(); +function export_method_call( $method ) { + $data = array( + 'name' => $method->getName(), + 'line' => $method->getLine(), + 'end_line' => $method->getLine(), + 'static' => $method->isStatic(), + ); - foreach ( $arguments as $argument ) { - $output[] = array( - 'name' => $argument->getName(), - 'default' => $argument->getDefault(), - 'type' => $argument->getType(), - ); + if ( method_exists( $method, 'getClass' ) ) { + $data['class'] = $method->getClass(); } - return $output; + return $data; } /** - * @param PropertyReflector[] $properties + * Export arguments to legacy format. * - * @return array + * @param array $parameters Parameters data from modern parser. + * @return array Legacy format arguments. */ -function export_properties( array $properties ) { - $out = array(); - - foreach ( $properties as $property ) { - $out[] = array( - 'name' => $property->getName(), - 'line' => $property->getLineNumber(), - 'end_line' => $property->getNode()->getAttribute( 'endLine' ), - 'default' => $property->getDefault(), -// 'final' => $property->isFinal(), - 'static' => $property->isStatic(), - 'visibility' => $property->getVisibility(), - 'doc' => export_docblock( $property ), +function export_arguments( $parameters ) { + $arguments = array(); + + foreach ( $parameters as $param ) { + $arguments[] = array( + 'name' => '$' . $param['name'], // Add $ prefix for variable names + 'type' => $param['type'] ?? '', // Use empty string instead of null + 'default' => $param['default'], + // Note: 'line' field not included in legacy format ); } - return $out; + return $arguments; } /** - * @param MethodReflector[] $methods + * Export docblock from parsed data to legacy format. * - * @return array + * @param array|null $docblock_data Parsed docblock data. + * @return array Legacy format docblock. */ -function export_methods( array $methods ) { - $output = array(); - - foreach ( $methods as $method ) { - - $method_data = array( - 'name' => $method->getShortName(), - 'namespace' => $method->getNamespace(), - 'aliases' => $method->getNamespaceAliases(), - 'line' => $method->getLineNumber(), - 'end_line' => $method->getNode()->getAttribute( 'endLine' ), - 'final' => $method->isFinal(), - 'abstract' => $method->isAbstract(), - 'static' => $method->isStatic(), - 'visibility' => $method->getVisibility(), - 'arguments' => export_arguments( $method->getArguments() ), - 'doc' => export_docblock( $method ), +function export_docblock_from_data( $docblock_data ) { + if ( ! $docblock_data ) { + return array( + 'description' => '', + 'long_description' => '', + 'tags' => array(), ); + } - if ( ! empty( $method->uses ) ) { - $method_data['uses'] = export_uses( $method->uses ); + $tags = array(); + if ( ! empty( $docblock_data['tags'] ) ) { + foreach ( $docblock_data['tags'] as $tag_name => $tag_values ) { + foreach ( $tag_values as $value ) { + $tag_data = array( + 'name' => $tag_name, + 'content' => $value, + ); + + // Parse @param and @return tags to extract types and variables + if ( in_array( $tag_name, array( 'param', 'return' ), true ) ) { + $parsed_tag = export_parse_tag( $tag_name, $value ); + $tag_data = array_merge( $tag_data, $parsed_tag ); + } - if ( ! empty( $method->uses['hooks'] ) ) { - $method_data['hooks'] = export_hooks( $method->uses['hooks'] ); + $tags[] = $tag_data; } } + } - $output[] = $method_data; + // Format descriptions according to legacy expectations: + // - description (summary) should be plain text + // - long_description should be wrapped in HTML paragraphs with linebreaks removed + $description = $docblock_data['summary'] ?? ''; + $long_description = $docblock_data['description'] ?? ''; + + if ( $long_description ) { + // Remove linebreaks and normalize whitespace + $long_description = preg_replace( '/\s+/', ' ', trim( $long_description ) ); + if ( ! str_contains( $long_description, '

' ) ) { + $long_description = '

' . $long_description . '

'; + } } - return $output; + return array( + 'description' => $description, + 'long_description' => $long_description, + 'tags' => $tags, + ); } /** - * Export the list of elements used by a file or structure. - * - * @param array $uses { - * @type Function_Call_Reflector[] $functions The functions called. - * } + * Legacy function for backward compatibility. + * Export docblock from old-style reflector. * - * @return array + * @param object $reflector Legacy reflector object. + * @return array Docblock data. */ -function export_uses( array $uses ) { - $out = array(); - - // Ignore hooks here, they are exported separately. - unset( $uses['hooks'] ); - - foreach ( $uses as $type => $used_elements ) { - - /** @var MethodReflector|FunctionReflector $element */ - foreach ( $used_elements as $element ) { - - $name = $element->getName(); - - switch ( $type ) { - case 'methods': - $out[ $type ][] = array( - 'name' => $name[1], - 'class' => $name[0], - 'static' => $element->isStatic(), - 'line' => $element->getLineNumber(), - 'end_line' => $element->getNode()->getAttribute( 'endLine' ), - ); - break; - - default: - case 'functions': - $out[ $type ][] = array( - 'name' => $name, - 'line' => $element->getLineNumber(), - 'end_line' => $element->getNode()->getAttribute( 'endLine' ), - ); - - if ( '_deprecated_file' === $name - || '_deprecated_function' === $name - || '_deprecated_argument' === $name - || '_deprecated_hook' === $name - ) { - $arguments = $element->getNode()->args; - - $out[ $type ][0]['deprecation_version'] = $arguments[1]->value->value; - } - - break; - } - } - } - - return $out; +function export_docblock( $reflector ) { + // This function exists for backward compatibility + // but shouldn't be called with our new architecture + return array( + 'description' => '', + 'long_description' => '', + 'tags' => array(), + ); } /** - * Format the given description with Markdown. + * Parse a docblock tag into the legacy format. * - * @param string $description Description. - * @return string Description as Markdown if the Parsedown class exists, otherwise return - * the given description text. + * @param string $tag_name The tag name (param, return, etc). + * @param string $value The tag value string. + * @return array Additional tag fields for the legacy format. */ -function format_description( $description ) { - if ( class_exists( 'Parsedown' ) ) { - $parsedown = \Parsedown::instance(); - $description = $parsedown->line( $description ); +function export_parse_tag( $tag_name, $value ) { + $result = array(); + + if ( 'param' === $tag_name ) { + // Parse @param type $variable description + if ( preg_match( '/^(\S+)\s+(\$\w+)\s+(.*)$/', $value, $matches ) ) { + $result['types'] = array( $matches[1] ); + $result['variable'] = $matches[2]; + $result['content'] = $matches[3]; + } elseif ( preg_match( '/^(\S+)\s+(.*)$/', $value, $matches ) ) { + $result['types'] = array( $matches[1] ); + $result['content'] = $matches[2]; + } + } elseif ( 'return' === $tag_name ) { + // Parse @return type description + if ( preg_match( '/^(\S+)\s+(.*)$/', $value, $matches ) ) { + $result['types'] = array( $matches[1] ); + $result['content'] = $matches[2]; + } } - - $description = fix_newlines( $description ); - - return $description; -} + + return $result; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bc5c93a..ad8d63d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,445 @@ "version": "1.0.0", "license": "GPL-2.0-or-later", "dependencies": { - "@wordpress/env": "^9.0.0", + "@wordpress/env": "^10.0.0", "npm-run-all": "^4.1.5" } }, + "node_modules/@inquirer/checkbox": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.9.tgz", + "integrity": "sha512-DBJBkzI5Wx4jFaYm221LHvAhpKYkhVS0k9plqHwaHhofGNxvYB7J3Bz8w+bFJ05zaMb0sZNHo4KdmENQFlNTuQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.13.tgz", + "integrity": "sha512-EkCtvp67ICIVVzjsquUiVSd+V5HRGOGQfsqA4E4vMWhYnB7InUL0pa0TIWt1i+OfP16Gkds8CdIu6yGZwOM1Yw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.1.14", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.14.tgz", + "integrity": "sha512-Ma+ZpOJPewtIYl6HZHZckeX1STvDnHTCB2GVINNUlSEn2Am6LddWwfPkIGY0IUFVjUUrr/93XlBwTK6mfLjf0A==", + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core/node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@inquirer/core/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.14.tgz", + "integrity": "sha512-yd2qtLl4QIIax9DTMZ1ZN2pFrrj+yL3kgIWxm34SS6uwCr0sIhsNyudUjAo5q3TqI03xx4SEBkUJqZuAInp9uA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.16.tgz", + "integrity": "sha512-oiDqafWzMtofeJyyGkb1CTPaxUkjIcSxePHHQCfif8t3HV9pHcw1Kgdw3/uGpDvaFfeTluwQtWiqzPVjAqS3zA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz", + "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.0.tgz", + "integrity": "sha512-opqpHPB1NjAmDISi3uvZOTrjEEU5CWVu/HBkDby8t93+6UxYX0Z7Ps0Ltjm5sZiEbWenjubwUkivAEYQmy9xHw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.16.tgz", + "integrity": "sha512-kMrXAaKGavBEoBYUCgualbwA9jWUx2TjMA46ek+pEKy38+LFpL9QHlTd8PO2kWPUgI/KB+qi02o4y2rwXbzr3Q==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.16.tgz", + "integrity": "sha512-g8BVNBj5Zeb5/Y3cSN+hDUL7CsIFDIuVxb9EPty3lkxBaYpjL5BNRKSYOF9yOLe+JOcKFd+TSVeADQ4iSY7rbg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.6.0.tgz", + "integrity": "sha512-jAhL7tyMxB3Gfwn4HIJ0yuJ5pvcB5maYUcouGcgd/ub79f9MqZ+aVnBtuFf+VC2GTkCBF+R+eo7Vi63w5VZlzw==", + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.1.9", + "@inquirer/confirm": "^5.1.13", + "@inquirer/editor": "^4.2.14", + "@inquirer/expand": "^4.0.16", + "@inquirer/input": "^4.2.0", + "@inquirer/number": "^3.0.16", + "@inquirer/password": "^4.0.16", + "@inquirer/rawlist": "^4.1.4", + "@inquirer/search": "^3.0.16", + "@inquirer/select": "^4.2.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.4.tgz", + "integrity": "sha512-5GGvxVpXXMmfZNtvWw4IsHpR7RzqAR624xtkPd1NxxlV5M+pShMqzL4oRddRkg8rVEOK9fKdJp1jjVML2Lr7TQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.16.tgz", + "integrity": "sha512-POCmXo+j97kTGU6aeRjsPyuCpQQfKcMXdeTMw708ZMtWrj5aykZvlUxH4Qgz3+Y1L/cAVZsSpA+UgZCu2GMOMg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.2.4.tgz", + "integrity": "sha512-unTppUcTjmnbl/q+h8XeQDhAqIOmwWYWNyiiP2e3orXrg6tOaa5DHXja9PChCSbChOsktyKgOieRZFnajzxoBg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz", + "integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@kwsites/file-exists": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", @@ -47,6 +482,16 @@ "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -110,25 +555,30 @@ } }, "node_modules/@wordpress/env": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-9.0.0.tgz", - "integrity": "sha512-Fyec0k5N7kaXVIpTnJ2zO/n3CIiq8cPDsUaFoLVKI+yv2cVuq1dSQyc1lLXB9Gu+SNvmfbq7nNVVmNfKgpAkrw==", + "version": "10.26.0", + "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-10.26.0.tgz", + "integrity": "sha512-pEeQgYp5plWWB79/MgHthMB4bR/e6VKtP4KUzspZefZzbXuoOlrdWVGwzdEJfKSoxjaXg5WTm2pDyo2YVCkzCQ==", + "license": "GPL-2.0-or-later", "dependencies": { + "@inquirer/prompts": "^7.2.0", "chalk": "^4.0.0", "copy-dir": "^1.3.0", "docker-compose": "^0.24.3", "extract-zip": "^1.6.7", "got": "^11.8.5", - "inquirer": "^7.1.0", "js-yaml": "^3.13.1", "ora": "^4.0.2", - "rimraf": "^3.0.2", + "rimraf": "^5.0.10", "simple-git": "^3.5.0", "terminal-link": "^2.0.0", "yargs": "^17.3.0" }, "bin": { "wp-env": "bin/wp-env" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" } }, "node_modules/ansi-escapes": { @@ -287,6 +737,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -301,7 +752,8 @@ "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "license": "MIT" }, "node_modules/cli-cursor": { "version": "3.1.0", @@ -326,11 +778,12 @@ } }, "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", "engines": { - "node": ">= 10" + "node": ">= 12" } }, "node_modules/cliui": { @@ -517,6 +970,12 @@ "node": ">= 6.0.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -651,6 +1110,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "license": "MIT", "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", @@ -682,32 +1142,100 @@ "pend": "~1.2.0" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", "dependencies": { - "escape-string-regexp": "^1.0.5" + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=8" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/foreground-child/node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "node_modules/foreground-child/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } }, "node_modules/function-bind": { "version": "1.1.2", @@ -794,19 +1322,44 @@ } }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -966,6 +1519,7 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -973,43 +1527,11 @@ "node": ">=0.10.0" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/internal-slot": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", @@ -1233,6 +1755,21 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -1277,11 +1814,6 @@ "node": ">=4" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, "node_modules/log-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", @@ -1357,6 +1889,12 @@ "node": ">=8" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", @@ -1400,6 +1938,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -1620,6 +2167,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1632,6 +2180,12 @@ "node": ">=8" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -1644,14 +2198,6 @@ "node": ">=4" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -1665,6 +2211,22 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -1821,38 +2383,20 @@ } }, "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "license": "ISC", "dependencies": { - "glob": "^7.1.3" + "glob": "^10.3.7" }, "bin": { - "rimraf": "bin.js" + "rimraf": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, "node_modules/safe-array-concat": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", @@ -1899,7 +2443,8 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/semver": { "version": "5.7.2", @@ -2071,6 +2616,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.padend": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.5.tgz", @@ -2140,6 +2700,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -2197,15 +2770,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "license": "MIT", "dependencies": { "os-tmpdir": "~1.0.2" }, @@ -2213,11 +2782,6 @@ "node": ">=0.6.0" } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", @@ -2396,6 +2960,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2450,6 +3032,18 @@ "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 5f190df..56b3fa8 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,16 @@ }, "homepage": "https://github.com/wordpress/phpdoc-parser#readme", "dependencies": { - "@wordpress/env": "^9.0.0", + "@wordpress/env": "^10.0.0", "npm-run-all": "^4.1.5" - } + }, + "engines": { + "node": ">=20.0.0", + "npm": ">=9.0.0" + }, + "os": [ + "darwin", + "linux", + "win32" + ] } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 71b4011..d0e7d26 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,18 +1,26 @@ - + + - - - tests/phpunit/tests/ - - + + + tests/phpunit/tests/ + + - - - lib - - + + + lib + + diff --git a/plugin.php b/plugin.php index e858aec..19235a0 100644 --- a/plugin.php +++ b/plugin.php @@ -20,8 +20,11 @@ $wp_parser->on_load(); } -register_activation_hook( __FILE__, array( 'P2P_Storage', 'init' ) ); -register_activation_hook( __FILE__, array( 'P2P_Storage', 'install' ) ); +// Only register P2P activation hooks if the class exists (WordPress plugin active) +if ( class_exists( 'P2P_Storage' ) ) { + register_activation_hook( __FILE__, array( 'P2P_Storage', 'init' ) ); + register_activation_hook( __FILE__, array( 'P2P_Storage', 'install' ) ); +} // TODO safer handling for uninstall //register_uninstall_hook( __FILE__, array( 'P2P_Storage', 'uninstall' ) ); diff --git a/tests/phpunit/includes/export-testcase.php b/tests/phpunit/includes/export-testcase.php index d74bf9f..88a061f 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/wp-cli.yml b/wp-cli.yml new file mode 100644 index 0000000..0f43041 --- /dev/null +++ b/wp-cli.yml @@ -0,0 +1,3 @@ +url: http://localhost:8888 +user: admin +quiet: true \ No newline at end of file